- remove some incorrect prototypes from server.c
[kai/samba.git] / source3 / nameserv.c
index 802b98ec0a0b6a829cb332dfc7f8e487fc2e37bc..b6bc8a4f06085c5e448b28a23695b1d4dc0ce0a5 100644 (file)
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    
+   Revision History:
+
+   14 jan 96: lkcl@pires.co.uk
+   added multiple workgroup domain master support
+
 */
 
 #include "includes.h"
 #include "loadparm.h"
-#include "nameserv.h"
+#include "localnet.h"
 
 
-static void queue_packet(struct packet_struct *packet);
-void process(void);
-static void dump_names(void);
-static void announce_request(char *group);
-void sync_browse_lists(char *name,int name_type,char *myname,
-                      char *domain,struct in_addr ip);
+enum name_search { FIND_SELF, FIND_GLOBAL };
 
 extern int DEBUGLEVEL;
 
-extern pstring debugf;
-pstring servicesf = CONFIGFILE;
-
 extern pstring scope;
-
 extern BOOL CanRecurse;
+extern pstring myname;
+extern struct in_addr ipzero;
 
-extern struct in_addr myip;
-extern struct in_addr bcast_ip;
-extern struct in_addr Netmask;
-extern pstring myhostname;
-static pstring host_file;
-static pstring myname="";
-
-static int ClientNMB= -1;
-static int ClientDGRAM= -1;
-
-static BOOL needannounce=True;
-
-/* this is our name database */
-static struct name_record *namelist = NULL;
-
-/* list of servers to be returned by NetServerEnum */
-static struct server_record *serverlist = NULL;
-
-/* this is the domain list. For the moment we will assume that our
-   primary domain is the first one listed in this list */
-static struct domain_record *domainlist = NULL;
-
-/* are we running as a daemon ? */
-static BOOL is_daemon = False;
-
-/* machine comment for host announcements */
-static pstring ServerComment="";
-
-static BOOL got_bcast = False;
-static BOOL got_myip = False;
-static BOOL got_nmask = False;
-
-static BOOL updatedlists = False;
-static int  updatecount=0;
-
-/* what server type are we currently */
-static int ServerType = 
-SV_TYPE_WORKSTATION | SV_TYPE_SERVER | SV_TYPE_TIME_SOURCE |
-SV_TYPE_SERVER_UNIX |
-SV_TYPE_PRINTQ_SERVER | SV_TYPE_POTENTIAL_BROWSER;
-
-/* here are my election parameters */
-
-/* NTAS uses 2, NT uses 1, WfWg uses 0 */
-#define MAINTAIN_LIST 1
-#define ELECTION_VERSION 1
-
-static BOOL RunningElection = False;
-static BOOL needelection = False;
-static int ElectionCount = 0;
-static int StartupTime =0;
-
-
-/* WfWg uses 01040b01 */
-/* Win95 uses 01041501 */
-/* NTAS uses ?? */
-static uint32 ElectionCriterion = (MAINTAIN_LIST<<1)|(ELECTION_VERSION<<8);
-
-/* we currently support being the master for just one group. Being the
-   master for more than one group might be tricky as NetServerEnum is
-   often asked for a list without naming the group */
-static fstring PrimaryGroup="";
-
-#define AM_MASTER (PrimaryGroup[0] && (ServerType & SV_TYPE_MASTER_BROWSER))
-
-#define MSBROWSE "\001\002__MSBROWSE__\002"
+/* netbios names database */
+struct name_record *namelist;
 
 #define GET_TTL(ttl) ((ttl)?MIN(ttl,lp_max_ttl()):lp_max_ttl())
 
-#define BROWSE_MAILSLOT "\\MAILSLOT\\BROWSE"
-
-/****************************************************************************
-catch a sighup
-****************************************************************************/
-static int sig_hup()
-{
-  BlockSignals(True);
-
-  DEBUG(0,("Got SIGHUP (reload not implemented)\n"));
-  dump_names();
-  reload_services(True);
-
-  BlockSignals(False);
-#ifndef DONT_REINSTALL_SIG
-  signal(SIGHUP,SIGNAL_CAST sig_hup);
-#endif
-  return(0);
-}
-
-/****************************************************************************
-catch a sigpipe
-****************************************************************************/
-static int sig_pipe()
-{
-  BlockSignals(True);
-
-  DEBUG(0,("Got SIGPIPE\n"));
-  if (!is_daemon)
-    exit(1);
-  BlockSignals(False);
-  return(0);
-}
-
-#if DUMP_CORE
-/*******************************************************************
-prepare to dump a core file - carefully!
-********************************************************************/
-static BOOL dump_core(void)
-{
-  char *p;
-  pstring dname;
-  strcpy(dname,debugf);
-  if ((p=strrchr(dname,'/'))) *p=0;
-  strcat(dname,"/corefiles");
-  mkdir(dname,0700);
-  sys_chown(dname,getuid(),getgid());
-  chmod(dname,0700);
-  if (chdir(dname)) return(False);
-  umask(~(0700));
-
-#ifndef NO_GETRLIMIT
-#ifdef RLIMIT_CORE
-  {
-    struct rlimit rlp;
-    getrlimit(RLIMIT_CORE, &rlp);
-    rlp.rlim_cur = MAX(4*1024*1024,rlp.rlim_cur);
-    setrlimit(RLIMIT_CORE, &rlp);
-    getrlimit(RLIMIT_CORE, &rlp);
-    DEBUG(3,("Core limits now %d %d\n",rlp.rlim_cur,rlp.rlim_max));
-  }
-#endif
-#endif
-
-
-  DEBUG(0,("Dumping core in %s\n",dname));
-  return(True);
-}
-#endif
-
-
-/****************************************************************************
-possibly continue after a fault
-****************************************************************************/
-static void fault_continue(void)
-{
-  static int errcount=1;
-
-  errcount--;
-
-  if (is_daemon && errcount)
-    process();
-
-#if DUMP_CORE
-    if (dump_core()) return;
-#endif
-
-  return;
-}
-
-
-/*******************************************************************
-  wrapper to get the DC
-  ******************************************************************/
-static char *domain_controller(void)
-{
-  char *dc = lp_domain_controller();
-  /* so many people mistake this for a bool that we need to handle it. sigh. */
-  if (!*dc || strequal(dc,"yes") || strequal(dc,"true"))
-    strcpy(dc,myname);
-  return(dc);
-}
-
-
 
 /****************************************************************************
   true if two netbios names are equal
@@ -232,7 +62,8 @@ static void add_name(struct name_record *n)
 {
   struct name_record *n2;
 
-  if (!namelist) {
+  if (!namelist)
+  {
     namelist = n;
     n->prev = NULL;
     n->next = NULL;
@@ -246,58 +77,18 @@ static void add_name(struct name_record *n)
   n->prev = n2;
 }
 
-/****************************************************************************
-  add a domain into the list
-  **************************************************************************/
-static void add_domain(struct domain_record *d)
-{
-  struct domain_record *d2;
-
-  if (!domainlist) {
-    domainlist = d;
-    d->prev = NULL;
-    d->next = NULL;
-    return;
-  }
-
-  for (d2 = domainlist; d2->next; d2 = d2->next) ;
-
-  d2->next = d;
-  d->next = NULL;
-  d->prev = d2;
-}
-
-
-/****************************************************************************
-  add a server into the list
-  **************************************************************************/
-static void add_server(struct server_record *s)
-{
-  struct server_record *s2;
-
-  if (!serverlist) {
-    serverlist = s;
-    s->prev = NULL;
-    s->next = NULL;
-    return;
-  }
-
-  for (s2 = serverlist; s2->next; s2 = s2->next) ;
-
-  s2->next = s;
-  s->next = NULL;
-  s->prev = s2;
-}
-
 /****************************************************************************
   remove a name from the namelist. The pointer must be an element just 
   retrieved
   **************************************************************************/
-static void remove_name(struct name_record *n)
+void remove_name(struct name_record *n)
 {
   struct name_record *nlist = namelist;
+
   while (nlist && nlist != n) nlist = nlist->next;
-  if (nlist) {
+
+  if (nlist)
+  {
     if (nlist->next) nlist->next->prev = nlist->prev;
     if (nlist->prev) nlist->prev->next = nlist->next;
     free(nlist);
@@ -305,48 +96,80 @@ static void remove_name(struct name_record *n)
 }
 
 /****************************************************************************
-  find a name in the namelist 
+  find a name in the domain database namelist 
+  search can be:
+  FIND_SELF   - look for names the samba server has added for itself
+  FIND_GLOBAL - the name can be anyone. first look on the client's
+                subnet, then the server's subnet, then all subnets.
   **************************************************************************/
-static struct name_record *find_name(struct nmb_name *n)
+static struct name_record *find_name_search(struct nmb_name *name, enum name_search search,
+                                           struct in_addr ip)
 {
-  struct name_record *ret;
-  for (ret = namelist; ret; ret = ret->next)
-    if (name_equal(&ret->name,n)) return(ret);
+       struct name_record *ret;
+
+       /* any number of winpopup names can be added. must search by ip as well */
+       if (name->name_type != 0x3) ip = ipzero;
+
+       for (ret = namelist; ret; ret = ret->next)
+       {
+               if (name_equal(&ret->name,name))
+               {
+                       /* self search: self names only */
+                       if (search == FIND_SELF && ret->source != SELF) continue;
+
+                       if (zero_ip(ip) || ip_equal(ip, ret->ip))
+                       {
+                               return ret;
+                       }
+               }
+       }
 
-  return(NULL);
+       return NULL;
 }
 
+
 /****************************************************************************
   dump a copy of the name table
   **************************************************************************/
-static void dump_names(void)
+void dump_names(void)
 {
-  time_t t = time(NULL);
-  struct name_record *n;
-  struct domain_record *d;
+       struct name_record *n;
+       time_t t = time(NULL);
 
-  DEBUG(3,("Dump of local name table:\n"));
+       DEBUG(3,("Dump of local name table:\n"));
 
-  for (n = namelist; n; n = n->next) {
-    DEBUG(3,("%s %s TTL=%d Unique=%s\n",
-            namestr(&n->name),
-            inet_ntoa(n->ip),
-            n->death_time?n->death_time-t:0,
-            BOOLSTR(n->unique)));
-    }
+       for (n = namelist; n; n = n->next)
+       {
+               DEBUG(3,("%s %s TTL=%d NBFLAGS=%2x\n",
+                       namestr(&n->name),
+                       inet_ntoa(n->ip),
+                       n->death_time?n->death_time-t:0,
+                               n->nb_flags));
+       }
+}
 
-  DEBUG(3,("\nDump of domain list:\n"));
-  for (d = domainlist; d; d = d->next)
-    DEBUG(3,("%s %s\n",d->name,inet_ntoa(d->bcast_ip)));
+
+/****************************************************************************
+  remove an entry from the name list
+  ****************************************************************************/
+void remove_netbios_name(char *name,int type, enum name_source source,
+                        struct in_addr ip)
+{
+       struct nmb_name nn;
+       struct name_record *n;
+
+       make_nmb_name(&nn, name, type, scope);
+       n = find_name_search(&nn, FIND_GLOBAL, ip);
+
+       if (n && n->source == source) remove_name(n);
 }
 
 
 /****************************************************************************
-  add a host entry to the name list
+  add an entry to the name list
   ****************************************************************************/
-static struct name_record *add_host_entry(char *name,int type,BOOL unique,int ttl,
-                                         enum name_source source,
-                                         struct in_addr ip)
+struct name_record *add_netbios_entry(char *name, int type, int nb_flags, int ttl,
+                                     enum name_source source, struct in_addr ip)
 {
   struct name_record *n;
   struct name_record *n2=NULL;
@@ -357,1364 +180,431 @@ static struct name_record *add_host_entry(char *name,int type,BOOL unique,int tt
   bzero((char *)n,sizeof(*n));
 
   make_nmb_name(&n->name,name,type,scope);
-  if ((n2=find_name(&n->name))) {
+
+  if ((n2 = find_name_search(&n->name, FIND_GLOBAL, ip)))
+  {
     free(n);
     n = n2;
   }
 
   if (ttl) n->death_time = time(NULL)+ttl*3;
   n->ip = ip;
-  n->unique = unique;
+  n->nb_flags = nb_flags;
   n->source = source;
   
   if (!n2) add_name(n);
 
-  DEBUG(3,("Added host entry %s at %s ttl=%d unique=%s\n",
-          namestr(&n->name),inet_ntoa(ip),ttl,BOOLSTR(unique)));
+  DEBUG(3,("Added netbios name %s at %s ttl=%d nb_flags=%2x\n",
+               namestr(&n->name),inet_ntoa(ip),ttl,nb_flags));
 
   return(n);
 }
 
 
 /****************************************************************************
-  add a domain entry
+  remove an entry from the name list
   ****************************************************************************/
-static struct domain_record *add_domain_entry(char *name,struct in_addr ip)
+void remove_name_entry(char *name,int type)
 {
-  struct domain_record *d;
-
-  d = (struct domain_record *)malloc(sizeof(*d));
-
-  if (!d) return(NULL);
-
-  bzero((char *)d,sizeof(*d));
-
-  if (zero_ip(ip)) ip = bcast_ip;
-
-  StrnCpy(d->name,name,sizeof(d->name)-1);
-  d->bcast_ip = ip;
-
-  if (!PrimaryGroup[0] && ip_equal(bcast_ip,ip) && name[0] != '*') {
-    strcpy(PrimaryGroup,name);
-    strupper(PrimaryGroup);
-    DEBUG(3,("Setting primary group to %s (%s)\n",PrimaryGroup,inet_ntoa(ip)));
-  }
-
-  add_domain(d);
-
-  ip = *interpret_addr2("255.255.255.255");
-  if (name[0] != '*') add_host_entry(name,0x1e,False,0,SELF,ip);         
-
-  DEBUG(3,("Added domain entry %s at %s\n",
-          name,inet_ntoa(ip)));
-
-  return(d);
+  if (lp_wins_support())
+    {
+      /* we are a WINS server. */
+      remove_netbios_name(name,type,SELF,myip);
+    }
+  else
+    {
+      struct in_addr ip;
+      ip = ipzero;
+      
+      queue_netbios_pkt_wins(ClientNMB,NMB_REL,NAME_RELEASE,
+                            name, type, 0,
+                            False, True, ip);
+    }
 }
 
+
 /****************************************************************************
-  add a server entry
+  add an entry to the name list
   ****************************************************************************/
-struct server_record *add_server_entry(char *name,int servertype,
-                                      int ttl,char *comment,BOOL replace)
+void add_name_entry(char *name,int type,int nb_flags)
 {
-  BOOL newentry=False;
-  struct server_record *s;
-
-  for (s = serverlist; s; s = s->next)
-    if (strequal(name,s->name)) break;
-
-  if (s && !replace) {
-    DEBUG(4,("Not replacing %s\n",name));
-    return(s);
-  }
-
-  updatedlists=True;
-
-  if (!s) {
-    newentry = True;
-    s = (struct server_record *)malloc(sizeof(*s));
+  /* always add our own entries */
+  add_netbios_entry(name,type,nb_flags,0,SELF,myip);
 
-    if (!s) return(NULL);
-
-    bzero((char *)s,sizeof(*s));
-  }
-
-  /* update the entry */
-  StrnCpy(s->name,name,sizeof(s->name)-1);
-  StrnCpy(s->comment,comment,sizeof(s->comment)-1);
-  s->servertype = servertype;
-  s->death_time = ttl?time(NULL)+ttl*3:0;
-  strupper(s->name);
-  if (s->servertype & SV_TYPE_DOMAIN_ENUM) strupper(s->comment);
-
-  if (!newentry) return(s);
-
-  add_server(s);
-
-  if (newentry) {
-    DEBUG(3,("Added server entry %s of type %x (%s)\n",
-            name,servertype,comment));
-  } else {
-    DEBUG(3,("Updated server entry %s of type %x (%s)\n",
-            name,servertype,comment));
-  }
-
-  return(s);
+  if (!lp_wins_support())
+    {
+      struct in_addr ip;
+      ip = ipzero;
+      
+      queue_netbios_pkt_wins(ClientNMB,NMB_REG,NAME_REGISTER,
+                            name, type, nb_flags,
+                            False, True, ip);
+    }
 }
 
 
 /****************************************************************************
   add the magic samba names, useful for finding samba servers
   **************************************************************************/
-static void add_my_names(void)
+void add_my_names(void)
 {
   struct in_addr ip;
 
-  ip = *interpret_addr2("0.0.0.0");
-
-  add_host_entry(myname,0x20,True,0,SELF,ip);
-  add_host_entry(myname,0x0,True,0,SELF,ip);
-  add_host_entry(myname,0x1f,True,0,SELF,ip); /* used for chat?? */
-  add_host_entry(myname,0x3,True,0,SELF,ip); /* used for winpopup */
-                                               
-  if (!domainlist)
-    add_domain_entry(lp_workgroup(),bcast_ip);
-  add_server_entry(myname,
-                  ServerType,
-                  0,ServerComment,True);
-
-  add_host_entry("__SAMBA__",0x20,True,0,SELF,ip);
-  add_host_entry("__SAMBA__",0x0,True,0,SELF,ip);
-
-  if (lp_preferred_master()) {
-    DEBUG(3,("Preferred master startup\n"));
-    needelection = True;
-    ElectionCriterion |= (1<<3);
-  }
-
-  ElectionCriterion |= (lp_os_level() << 24);
-}
-
-
-/*******************************************************************
-  write out browse.dat
-  ******************************************************************/
-static void write_browse_list(void)
-{
-  struct server_record *s;
-  pstring fname,fnamenew;
-  FILE *f;
+  ip = ipzero;
   
-  updatecount++;
-
-  strcpy(fname,lp_lockdir());
-  trim_string(fname,NULL,"/");
-  strcat(fname,"/");
-  strcat(fname,SERVER_LIST);
-  strcpy(fnamenew,fname);
-  strcat(fnamenew,".");
+  add_name_entry(myname,0x20,NB_ACTIVE);
+  add_name_entry(myname,0x03,NB_ACTIVE);
+  add_name_entry(myname,0x00,NB_ACTIVE);
+  add_name_entry(myname,0x1f,NB_ACTIVE);
   
-  f = fopen(fnamenew,"w");
+  add_netbios_entry("*",0x0,NB_ACTIVE,0,SELF,ip);
+  add_netbios_entry("__SAMBA__",0x20,NB_ACTIVE,0,SELF,ip);
+  add_netbios_entry("__SAMBA__",0x00,NB_ACTIVE,0,SELF,ip);
   
-  if (!f) {
-    DEBUG(4,("Can't open %s - %s\n",fnamenew,strerror(errno)));
-    return;
-  }
-  
-  for (s=serverlist; s ; s = s->next) {
-    /* don't list domains I don't have a master for */
-    if ((s->servertype & SV_TYPE_DOMAIN_ENUM) && !s->comment[0]) continue;
-       
-    fprintf(f,"\"%s\"\t%08x\t\"%s\"\n",s->name,s->servertype,s->comment);
+  if (lp_wins_support()) {
+    add_netbios_entry(inet_ntoa(myip),0x01,NB_ACTIVE,0,SELF,ip); /* nt as? */
   }
-  
-  
-  fclose(f);
-  chmod(fnamenew,0644);
-  /* unlink(fname); */
-  rename(fnamenew,fname);   
-  DEBUG(3,("Wrote browse list %s\n",fname));
 }
 
 /*******************************************************************
-  expire old names in the namelist and serverlist
+  refresh my own names
   ******************************************************************/
-static void expire_names(void)
+void refresh_my_names(time_t t)
 {
-  static time_t lastrun=0;
-  time_t t = time(NULL);
-  struct name_record *n;
-  struct name_record *next;
-  struct server_record *s;
-  struct server_record *nexts;
+  static time_t lasttime = 0;
 
-  if (!lastrun) lastrun = t;
-  if (t < lastrun + 5) return;
-  lastrun = t;
-
-  /* expire old names */
-  for (n = namelist; n; n = next) {
-    if (n->death_time && n->death_time < t) {
-      DEBUG(3,("Removing dead name %s\n",
-              namestr(&n->name)));
-      next = n->next;
-      if (n->prev) n->prev->next = n->next;
-      if (n->next) n->next->prev = n->prev;
-      if (namelist == n) namelist = n->next; 
-      free(n);
-    } else {
-      next = n->next;
-    }
-  }
+  if (t - lasttime < REFRESH_TIME) 
+    return;
+  lasttime = t;
 
-  /* expire old entries in the serverlist */
-  for (s = serverlist; s; s = nexts) {
-    if (s->death_time && s->death_time < t) {
-      DEBUG(3,("Removing dead server %s\n",s->name));
-      updatedlists = True;
-      nexts = s->next;
-      if (s->prev) s->prev->next = s->next;
-      if (s->next) s->next->prev = s->prev;
-      if (serverlist == s) serverlist = s->next; 
-      free(s);
-    } else {
-      nexts = s->next;
-    }
-  }
+  add_my_names();
 }
 
-
 /*******************************************************************
-  delete old names from the namelist
+  expires old names in the namelist
   ******************************************************************/
-static void housekeeping(void)
+void expire_names(time_t t)
 {
-  time_t t = time(NULL);
-
-  expire_names();
-
-  /* write out the browse.dat database for smbd to get */
-  if (updatedlists) {
-    write_browse_list();
-    updatedlists = False;
-  }
-
-  {
-    /* occasionally check to see if the master browser is around */
-    static time_t lastrun=0;
-    if (!lastrun) lastrun = t;
-    if (t < lastrun + 5*60) return;
-    lastrun = t;
-
-    if (!AM_MASTER && PrimaryGroup[0] &&
-       !name_query(ClientNMB,PrimaryGroup,0x1d,True,False,
-                   bcast_ip,NULL,queue_packet)) {
-      DEBUG(2,("Forcing election on %s\n",PrimaryGroup));
-      needelection = True;
+  struct name_record *n;
+  struct name_record *next;
+  
+  /* expire old names */
+  for (n = namelist; n; n = next)
+    {
+      if (n->death_time && n->death_time < t)
+       {
+         DEBUG(3,("Removing dead name %s\n", namestr(&n->name)));
+         
+         next = n->next;
+         
+         if (n->prev) n->prev->next = n->next;
+         if (n->next) n->next->prev = n->prev;
+         
+         if (namelist == n) namelist = n->next; 
+         
+         free(n);
+       }
+      else
+       {
+         next = n->next;
+       }
     }
-  }
 }
 
 
 /****************************************************************************
-  reload the services file
-  **************************************************************************/
-BOOL reload_services(BOOL test)
+response for a reg release received
+**************************************************************************/
+void response_name_release(struct packet_struct *p)
 {
-  BOOL ret;
-  extern fstring remote_machine;
-
-  strcpy(remote_machine,"nmbd");
-
-  if (lp_loaded())
+  struct nmb_packet *nmb = &p->packet.nmb;
+  char *name = nmb->question.question_name.name;
+  int   type = nmb->question.question_name.name_type;
+  
+  DEBUG(4,("response name release received\n"));
+  
+  if (nmb->header.rcode == 0 && nmb->answers->rdata)
     {
-      pstring fname;
-      strcpy(fname,lp_configfile());
-      if (file_exist(fname,NULL) && !strcsequal(fname,servicesf))
+      struct in_addr found_ip;
+      putip((char*)&found_ip,&nmb->answers->rdata[2]);
+      
+      if (ip_equal(found_ip, myip))
        {
-         strcpy(servicesf,fname);
-         test = False;
+         remove_netbios_name(name,type,SELF,found_ip);
        }
     }
-
-  if (test && !lp_file_list_changed())
-    return(True);
-
-  ret = lp_load(servicesf,True);
-
-  /* perhaps the config filename is now set */
-  if (!test)
-    reload_services(True);
-
-  return(ret);
+  else
+    {
+      DEBUG(1,("name registration for %s rejected!\n",
+              namestr(&nmb->question.question_name)));
+    }
 }
 
 
-
 /****************************************************************************
-load a netbios hosts file
+reply to a name release
 ****************************************************************************/
-static void load_hosts_file(char *fname)
+void reply_name_release(struct packet_struct *p)
 {
-  FILE *f = fopen(fname,"r");
-  pstring line;
-  if (!f) {
-    DEBUG(2,("Can't open lmhosts file %s\n",fname));
-    return;
-  }
-
-  while (!feof(f))
+  struct nmb_packet *nmb = &p->packet.nmb;
+  struct in_addr ip;
+  int rcode=0;
+  int opcode = nmb->header.opcode;  
+  int nb_flags = nmb->additional->rdata[0];
+  BOOL bcast = nmb->header.nm_flags.bcast;
+  struct name_record *n;
+  char rdata[6];
+  
+  putip((char *)&ip,&nmb->additional->rdata[2]);  
+  
+  DEBUG(3,("Name release on name %s rcode=%d\n",
+          namestr(&nmb->question.question_name),rcode));
+  
+  n = find_name_search(&nmb->question.question_name, FIND_GLOBAL, ip);
+  
+  /* XXXX under what conditions should we reject the removal?? */
+  if (n && n->nb_flags == nb_flags && ip_equal(n->ip,ip))
     {
-      if (!fgets_slash(line,sizeof(pstring),f)) continue;
+      /* success = True;
+        rcode = 6; */
       
-      if (*line == '#') continue;
-
-      {
-       BOOL group=False;
-       string ip,name,flags,extra;
-       char *ptr;
-       int count = 0;
-       struct in_addr ipaddr;
-       enum name_source source = LMHOSTS;
-
-       *ip = *name = *flags = *extra = 0;
-
-       ptr = line;
-
-       if (next_token(&ptr,ip,NULL)) ++count;
-       if (next_token(&ptr,name,NULL)) ++count;
-       if (next_token(&ptr,flags,NULL)) ++count;
-       if (next_token(&ptr,extra,NULL)) ++count;
-
-       if (count <= 0) continue;
-
-       if (count > 0 && count < 2)
-         {
-           DEBUG(0,("Ill formed hosts line [%s]\n",line));         
-           continue;
-         }
-
-       if (strchr(flags,'G') || strchr(flags,'S'))
-         group = True;
-
-       if (strchr(flags,'M') && !group) {
-         source = SELF;
-         strcpy(myname,name);
-       }
-
-       ipaddr = *interpret_addr2(ip);
-
-       if (group) {
-         add_domain_entry(name,ipaddr);
-       } else {
-         add_host_entry(name,0x20,True,0,source,ipaddr);
-       }
-      }
+      remove_name(n);
+      n = NULL;
     }
-
-  fclose(f);
+  
+  if (bcast) return;
+  
+  /*if (success)*/
+  {
+    rdata[0] = nb_flags;
+    rdata[1] = 0;
+    putip(&rdata[2],(char *)&ip);
+  }
+  
+  /* Send a NAME RELEASE RESPONSE */
+  reply_netbios_packet(p,nmb->header.name_trn_id,rcode,opcode,
+                      &nmb->question.question_name,
+                      nmb->question.question_type,
+                      nmb->question.question_class,
+                      0,
+                      rdata, 6 /*success ? 6 : 0*/);
+  /* XXXX reject packet never tested: cannot tell what to do */
 }
 
-/*******************************************************************
-  check if 2 IPs are on the same net
-  we will assume the local netmask, although this could be wrong XXXX
-  ******************************************************************/
-static BOOL same_net(struct in_addr ip1,struct in_addr ip2)
-{
-  unsigned long net1,net2,nmask;
-
-  nmask = ntohl(Netmask.s_addr);
-  net1 = ntohl(ip1.s_addr);
-  net2 = ntohl(ip2.s_addr);
-           
-  return((net1 & nmask) == (net2 & nmask));
-}
 
 /****************************************************************************
-  send an election packet
-  **************************************************************************/
-static void send_election(char *group,uint32 criterion,int timeup,char *name)
+response for a reg request received
+**************************************************************************/
+void response_name_reg(struct packet_struct *p)
 {
-  pstring outbuf;
-  char *p;
-
-  DEBUG(2,("Sending election to %s for workgroup %s\n",
-          inet_ntoa(bcast_ip),group));    
-
-  bzero(outbuf,sizeof(outbuf));
-  p = outbuf;
-  CVAL(p,0) = 8; /* election */
-  p++;
-
-  CVAL(p,0) = ELECTION_VERSION;
-  SIVAL(p,1,criterion);
-  SIVAL(p,5,timeup*1000); /* ms - despite the spec */
-  p += 13;
-  strcpy(p,name);
-  strupper(p);
-  p = skip_string(p,1);
-
-  send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
-                     name,group,0,0x1e,bcast_ip,myip);
+  struct nmb_packet *nmb = &p->packet.nmb;
+  char *name = nmb->question.question_name.name;
+  int   type = nmb->question.question_name.name_type;
+  
+  DEBUG(4,("response name registration received!\n"));
+  
+  if (nmb->header.rcode == 0 && nmb->answers->rdata)
+    {
+      int nb_flags = nmb->answers->rdata[0];
+      struct in_addr found_ip;
+      int ttl = nmb->answers->ttl;
+      enum name_source source = REGISTER;
+      
+      putip((char*)&found_ip,&nmb->answers->rdata[2]);
+      
+      if (ip_equal(found_ip, myip)) source = SELF;
+      
+      add_netbios_entry(name,type,nb_flags,ttl,source,found_ip);
+    }
+  else
+    {
+      DEBUG(1,("name registration for %s rejected!\n",
+              namestr(&nmb->question.question_name)));
+    }
 }
 
 
 /****************************************************************************
-  send a backup list response
-  **************************************************************************/
-static void send_backup_list(char *name,int token,struct nmb_name *to,
-                            struct in_addr ip)
+reply to a reg request
+**************************************************************************/
+void reply_name_reg(struct packet_struct *p)
 {
-  pstring outbuf;
-  char *p;
-
-  DEBUG(2,("Sending backup list to %s for workgroup %s\n",
-          inet_ntoa(ip),PrimaryGroup));           
-
-  bzero(outbuf,sizeof(outbuf));
-  p = outbuf;
-  CVAL(p,0) = 10; /* backup list response */
-  p++;
-
-  CVAL(p,0) = 1; /* count */
-  SIVAL(p,1,token);
-  p += 5; 
-  strcpy(p,name);
-  strupper(p);
-  p = skip_string(p,1) + 1;
-
-  send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
-                     myname,to->name,0,to->name_type,ip,myip);
-}
-
-
-/*******************************************************************
-  become the master browser
-  ******************************************************************/
-static void become_master(void)
-{
-  uint32 domain_type = SV_TYPE_DOMAIN_ENUM | SV_TYPE_SERVER_UNIX;
-  DEBUG(2,("Becoming master for %s\n",PrimaryGroup));
-
-  ServerType |= SV_TYPE_MASTER_BROWSER;
-  ServerType |= SV_TYPE_BACKUP_BROWSER;
-  ElectionCriterion |= 0x5;
-
-  add_host_entry(PrimaryGroup,0x1d,True,0,SELF,myip);
-  add_host_entry(PrimaryGroup,0x0,False,0,SELF,myip);
-  add_host_entry(MSBROWSE,1,False,0,SELF,myip);
-
-  if (lp_domain_master()) {
-    add_host_entry(myname,0x1b,True,0,SELF,myip);
-    add_host_entry(PrimaryGroup,0x1b,True,0,SELF,myip);
-    add_host_entry(PrimaryGroup,0x1c,False,0,SELF,myip);
-    ServerType |= SV_TYPE_DOMAIN_MASTER;
-    if (lp_domain_logons()) {
-      ServerType |= SV_TYPE_DOMAIN_CTRL;
-      ServerType |= SV_TYPE_DOMAIN_MEMBER;
-      domain_type |= SV_TYPE_DOMAIN_CTRL;
-    }
-  }
-
-  add_server_entry(PrimaryGroup,domain_type,0,myname,True);
-  add_server_entry(myname,ServerType,0,ServerComment,True);
-
-  announce_request(PrimaryGroup);
-
-  needannounce = True;
-}
-
-
-/*******************************************************************
-  unbecome the master browser
-  ******************************************************************/
-static void become_nonmaster(void)
-{
-  struct name_record *n;
-  struct nmb_name nn;
-
-  DEBUG(2,("Becoming non-master for %s\n",PrimaryGroup));
-
-  ServerType &= ~SV_TYPE_MASTER_BROWSER;
-  ServerType &= ~SV_TYPE_DOMAIN_CTRL;
-  ServerType &= ~SV_TYPE_DOMAIN_MASTER;
-
-  ElectionCriterion &= ~0x4;
-
-  make_nmb_name(&nn,PrimaryGroup,0x1d,scope);
-  n = find_name(&nn);
-  if (n && n->source == SELF) remove_name(n);
-
-  make_nmb_name(&nn,PrimaryGroup,0x1b,scope);
-  n = find_name(&nn);
-  if (n && n->source == SELF) remove_name(n);
-
-  make_nmb_name(&nn,MSBROWSE,1,scope);
-  n = find_name(&nn);
-  if (n && n->source == SELF) remove_name(n);
-}
-
-
-/*******************************************************************
-  run the election
-  ******************************************************************/
-static void run_election(void)
-{
-  time_t t = time(NULL);
-  static time_t lastime = 0;
-
-  if (!PrimaryGroup[0] || !RunningElection) return;
-
-  /* send election packets once a second */
-  if (lastime &&
-      t-lastime <= 0) return;
-
-  lastime = t;
-
-  send_election(PrimaryGroup,ElectionCriterion,t-StartupTime,myname);
-
-  if (ElectionCount++ < 4) return;
-   
-  /* I won! now what :-) */
-  RunningElection = False;
-  DEBUG(2,(">>> Won election on %s <<<\n",PrimaryGroup));
-  become_master();
-}
-
-
-/****************************************************************************
-  construct a host announcement unicast
-  **************************************************************************/
-static void announce_host(struct domain_record *d,char *my_name,char *comment)
-{
-  time_t t = time(NULL);
-  pstring outbuf;
-  char *p;
-  char *namep;
-  char *stypep;
-  char *commentp;
-  uint32 stype = ServerType;
-
-  if (needannounce) {
-    /* drop back to a max 3 minute announce - this is to prevent a
-       single lost packet from stuffing things up for too long */
-    d->announce_interval = MIN(d->announce_interval,3*60);
-    d->lastannounce_time = t - (d->announce_interval+1);
-  }
-
-  /* announce every minute at first then progress to every 12 mins */
-  if (d->lastannounce_time && 
-      (t - d->lastannounce_time) < d->announce_interval)
-    return;
-
-  if (d->announce_interval < 12*60) d->announce_interval += 60;
-  d->lastannounce_time = t;
-
-  DEBUG(2,("Sending announcement to %s for workgroup %s\n",
-          inet_ntoa(d->bcast_ip),d->name));
-
-  if (!strequal(PrimaryGroup,d->name) ||
-      !ip_equal(bcast_ip,d->bcast_ip)) {
-    stype &= ~(SV_TYPE_POTENTIAL_BROWSER | SV_TYPE_MASTER_BROWSER |
-              SV_TYPE_DOMAIN_MASTER | SV_TYPE_BACKUP_BROWSER |
-              SV_TYPE_DOMAIN_CTRL | SV_TYPE_DOMAIN_MEMBER);
-  }
-
-  if (!*comment) comment = "NoComment";
-  if (!*my_name) my_name = "NoName";
-
-  if (strlen(comment) > 43) comment[43] = 0;  
-
-  bzero(outbuf,sizeof(outbuf));
-  CVAL(outbuf,0) = 1; /* host announce */
-  p = outbuf+1;
-
-  CVAL(p,0) = updatecount;
-  SIVAL(p,1,d->announce_interval*1000); /* ms - despite the spec */
-  namep = p+5;
-  StrnCpy(p+5,my_name,16);
-  strupper(p+5);
-  CVAL(p,21) = 2; /* major version */
-  CVAL(p,22) = 2; /* minor version */
-  stypep = p+23;
-  SIVAL(p,23,stype);
-  SSVAL(p,27,0xaa55); /* browse signature */
-  SSVAL(p,29,1); /* browse version */
-  commentp = p+31;
-  strcpy(p+31,comment);
-  p += 31;
-  p = skip_string(p,1);
-
-  send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
-                     my_name,d->name,0,0x1d,d->bcast_ip,myip);
-
-  /* if I'm the master then I also need to do a local master and
-     domain announcement */
-
-  if (AM_MASTER &&
-      strequal(d->name,PrimaryGroup) &&
-      ip_equal(bcast_ip,d->bcast_ip)) {
-
-    /* do master announcements as well */
-    SIVAL(stypep,0,ServerType);
-
-    CVAL(outbuf,0) = 15; /* local master announce */
-    send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
-                       my_name,PrimaryGroup,0,0x1e,d->bcast_ip,myip);
-
-    CVAL(outbuf,0) = 12; /* domain announce */
-    StrnCpy(namep,PrimaryGroup,15);
-    strupper(namep);
-    StrnCpy(commentp,myname,15);
-    strupper(commentp);
-    SIVAL(stypep,0,(unsigned)0x80000000);
-    p = commentp + strlen(commentp) + 1;
-
-    send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
-                       my_name,MSBROWSE,0,1,d->bcast_ip,myip);
-  }
-}
-
-
-/****************************************************************************
-  send a announce request to the local net
-  **************************************************************************/
-static void announce_request(char *group)
-{
-  pstring outbuf;
-  char *p;
-
-  DEBUG(2,("Sending announce request to %s for workgroup %s\n",
-          inet_ntoa(bcast_ip),group));
-
-  bzero(outbuf,sizeof(outbuf));
-  p = outbuf;
-  CVAL(p,0) = 2; /* announce request */
-  p++;
-
-  CVAL(p,0) = 0; /* flags?? */
-  p++;
-  StrnCpy(p,myname,16);
-  strupper(p);
-  p = skip_string(p,1);
-
-  send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
-                     myname,group,0,0,bcast_ip,myip);
-}
-
-/****************************************************************************
-  announce myself as a master to the PDC
-  **************************************************************************/
-static void announce_master(char *group)
-{
-  static time_t last=0;
-  time_t t = time(NULL);
-  pstring outbuf;
-  char *p;
-  struct in_addr ip,pdc_ip;
-  fstring pdcname;
-  *pdcname = 0;
-
-  if (strequal(domain_controller(),myname)) return;
-
-  if (!AM_MASTER || (last && (t-last < 10*60))) return;
-  last = t;
-
-  ip = *interpret_addr2(domain_controller());
-
-  if (zero_ip(ip)) ip = bcast_ip;
-
-  if (!name_query(ClientNMB,PrimaryGroup,
-                 0x1b,False,False,ip,&pdc_ip,queue_packet)) {
-    DEBUG(2,("Failed to find PDC at %s\n",domain_controller()));
-    return;
-  }
-
-  name_status(ClientNMB,PrimaryGroup,0x1b,False,
-             pdc_ip,NULL,pdcname,queue_packet);
-
-  if (!pdcname[0]) {
-    DEBUG(3,("Can't find netbios name of PDC at %s\n",inet_ntoa(pdc_ip)));
-  } else {
-    sync_browse_lists(pdcname,0x20,myname,PrimaryGroup,pdc_ip);
-  }
-
-
-  DEBUG(2,("Sending master announce to %s for workgroup %s\n",
-          inet_ntoa(pdc_ip),group));
-
-  bzero(outbuf,sizeof(outbuf));
-  p = outbuf;
-  CVAL(p,0) = 13; /* announce request */
-  p++;
-
-  StrnCpy(p,myname,16);
-  strupper(p);
-  p = skip_string(p,1);
-
-  send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
-                     myname,PrimaryGroup,0x1b,0,pdc_ip,myip);
-}
-
-
-/*******************************************************************
-  am I listening on a name. Should check name_type as well 
-
-  This is primarily used to prevent us gathering server lists from
-  other workgroups we aren't a part of
-  ******************************************************************/
-static BOOL listening(struct nmb_name *n)
-{
-  if (!strequal(n->scope,scope)) return(False);
-
-  if (strequal(n->name,myname) ||
-      strequal(n->name,PrimaryGroup) ||
-      strequal(n->name,MSBROWSE))
-    return(True);
-
-  return(False);
-}
-
-
-/*******************************************************************
-  process a domain announcement frame
-
-  Announce frames come in 3 types. Servers send host announcements
-  (command=1) to let the master browswer know they are
-  available. Master browsers send local master announcements
-  (command=15) to let other masters and backups that they are the
-  master. They also send domain announcements (command=12) to register
-  the domain
-
-  The comment field of domain announcements contains the master
-  browser name. The servertype is used by NetServerEnum to select
-  resources. We just have to pass it to smbd (via browser.dat) and let
-  the client choose using bit masks.
-  ******************************************************************/
-static void process_announce(struct packet_struct *p,int command,char *buf)
-{
-  struct dgram_packet *dgram = &p->packet.dgram;
-  int update_count = CVAL(buf,0);
-  int ttl = IVAL(buf,1)/1000;
-  char *name = buf+5;
-  int osmajor=CVAL(buf,21);
-  int osminor=CVAL(buf,22);
-  uint32 servertype = IVAL(buf,23);
-  char *comment = buf+31;
-
-  name[15] = 0;  
-  comment[43] = 0;
-  
-  DEBUG(3,("Announce(%d) %s count=%d ttl=%d OS=(%d,%d) type=%08x comment=%s\n",
-          command,name,update_count,ttl,osmajor,osminor,
-          servertype,comment));
-
-  if (strequal(dgram->source_name.name,myname)) return;
-
-  if (!listening(&dgram->dest_name)) return;
-
-  ttl = GET_TTL(ttl);
-
-  /* add them to our browse list */
-  add_server_entry(name,servertype,ttl,comment,True);
-
-}
-
-/*******************************************************************
-  process a master announcement frame
-  ******************************************************************/
-static void process_master_announce(struct packet_struct *p,char *buf)
-{
-  struct dgram_packet *dgram = &p->packet.dgram;
-  char *name = buf;
-
-  name[15] = 0;
+  struct nmb_packet *nmb = &p->packet.nmb;
+  struct nmb_name *question = &nmb->question.question_name;
+  char *qname = nmb->question.question_name.name;
+  int name_type = nmb->question.question_name.name_type;
   
-  DEBUG(3,("Master Announce from %s (%s)\n",name,inet_ntoa(p->ip)));
-
-  if (strequal(dgram->source_name.name,myname)) return;
-
-  if (!AM_MASTER || !listening(&dgram->dest_name)) return;
-
-  /* merge browse lists with them */
-  if (lp_domain_master())
-    sync_browse_lists(name,0x20,myname,PrimaryGroup,p->ip);
-}
-
-
-/*******************************************************************
-  process a backup list request
-
-  A client send a backup list request to ask for a list of servers on
-  the net that maintain server lists for a domain. A server is then
-  chosen from this list to send NetServerEnum commands to to list
-  available servers.
-
-  Currently samba only sends back one name in the backup list, its
-  wn. For larger nets we'll have to add backups and send "become
-  backup" requests occasionally.
-  ******************************************************************/
-static void process_backup_list(struct packet_struct *p,char *buf)
-{
-  struct dgram_packet *dgram = &p->packet.dgram;
-  int count = CVAL(buf,0);
-  int token = IVAL(buf,1);
+  BOOL bcast = nmb->header.nm_flags.bcast;
   
-  DEBUG(3,("Backup request to %s token=%d\n",
-          namestr(&dgram->dest_name),
-          token));
-
-  if (strequal(dgram->source_name.name,myname)) return;
-
-  if (count <= 0) return;
-
-  if (!AM_MASTER || 
-      !strequal(PrimaryGroup,dgram->dest_name.name))
-    return;
-
-  if (!listening(&dgram->dest_name)) return;
-
-  send_backup_list(myname,token,
-                  &dgram->source_name,
-                  p->ip);
-}
-
-
-/*******************************************************************
-  work out if I win an election
-  ******************************************************************/
-static BOOL win_election(int version,uint32 criterion,int timeup,char *name)
-{  
-  time_t t = time(NULL);
-  uint32 mycriterion;
-  if (version > ELECTION_VERSION) return(False);
-  if (version < ELECTION_VERSION) return(True);
+  int ttl = GET_TTL(nmb->additional->ttl);
+  int nb_flags = nmb->additional->rdata[0];
+  BOOL group = (nb_flags&0x80);
+  int rcode = 0;  
+  int opcode = nmb->header.opcode;  
+  struct name_record *n = NULL;
+  int success = True;
+  char rdata[6];
+  struct in_addr ip, from_ip;
   
-  mycriterion = ElectionCriterion;
-
-  if (criterion > mycriterion) return(False);
-  if (criterion < mycriterion) return(True);
-
-  if (timeup > (t - StartupTime)) return(False);
-  if (timeup < (t - StartupTime)) return(True);
-
-  if (strcasecmp(myname,name) > 0) return(False);
+  putip((char *)&from_ip,&nmb->additional->rdata[2]);
+  ip = from_ip;
   
-  return(True);
-}
-
-
-/*******************************************************************
-  process a election packet
-
-  An election dynamically decides who will be the master. 
-  ******************************************************************/
-static void process_election(struct packet_struct *p,char *buf)
-{
-  struct dgram_packet *dgram = &p->packet.dgram;
-  int version = CVAL(buf,0);
-  uint32 criterion = IVAL(buf,1);
-  int timeup = IVAL(buf,5)/1000;
-  char *name = buf+13;
-
-  name[15] = 0;  
+  DEBUG(3,("Name registration for name %s at %s rcode=%d\n",
+          namestr(question),inet_ntoa(ip),rcode));
   
-  DEBUG(3,("Election request from %s vers=%d criterion=%08x timeup=%d\n",
-          name,version,criterion,timeup));
-
-  if (strequal(dgram->source_name.name,myname)) return;
-
-  if (!listening(&dgram->dest_name)) return;
-
-  if (win_election(version,criterion,timeup,name)) {
-    if (!RunningElection) {
-      needelection = True;
-      ElectionCount=0;
-    }
-  } else {
-    needelection = False;
-    if (RunningElection) {
-      RunningElection = False;
-      DEBUG(3,(">>> Lost election on %s <<<\n",PrimaryGroup));
-
-      /* if we are the master then remove our masterly names */
-      if (AM_MASTER)
-       become_nonmaster();
-    }
-  }
-}
-
-
-/*******************************************************************
-  process a announcement request
-
-  clients send these when they want everyone to send an announcement
-  immediately. This can cause quite a storm of packets!
-  ******************************************************************/
-static void process_announce_request(struct packet_struct *p,char *buf)
-{
-  struct dgram_packet *dgram = &p->packet.dgram;
-  int flags = CVAL(buf,0);
-  char *name = buf+1;
-
-  name[15] = 0;
-
-  DEBUG(3,("Announce request from %s flags=0x%X\n",name,flags));
-
-  if (strequal(dgram->source_name.name,myname)) return;
-
-  needannounce = True;
-}
-
-
-/****************************************************************************
-process a browse frame
-****************************************************************************/
-static void process_browse_packet(struct packet_struct *p,char *buf,int len)
-{
-  int command = CVAL(buf,0);
-  switch (command) 
+  if (group)
     {
-    case 1: /* host announce */
-    case 12: /* domain announce */
-    case 15: /* local master announce */
-      process_announce(p,command,buf+1);
-      break;
-
-    case 2: /* announce request */
-      process_announce_request(p,buf+1);
-      break;
-
-    case 8: /* election */
-      process_election(p,buf+1);
-      break;
-
-    case 9: /* get backup list */
-      process_backup_list(p,buf+1);
-      break;
-
-    case 13: /* master announcement */
-      process_master_announce(p,buf+1);
-      break;
+      /* apparently we should return 255.255.255.255 for group queries
+        (email from MS) */
+      ip = *interpret_addr2("255.255.255.255");
     }
-}
-
-
-/****************************************************************************
-  process a domain logon packet
-  **************************************************************************/
-static void process_logon_packet(struct packet_struct *p,char *buf,int len)
-{
-  char *logname,*q;
-  pstring outbuf;
-  struct dgram_packet *dgram = &p->packet.dgram;
-  int code;
-
-  if (!lp_domain_logons()) {
-    DEBUG(3,("No domain logons\n"));
-    return;
-  }
-  if (!listening(&dgram->dest_name)) {
-    DEBUG(4,("Not listening to that domain\n"));
-    return;
-  }
-
-  q = outbuf;
-  bzero(outbuf,sizeof(outbuf));
-
-  code = SVAL(buf,0);
-  switch (code) {
-  case 0:    
+  
+  /* see if the name already exists */
+  n = find_name_search(question, FIND_GLOBAL, from_ip);
+  
+  if (n)
     {
-      char *machine = buf+2;
-      char *user = skip_string(machine,1);
-      logname = skip_string(user,1);
-
-      SSVAL(q,0,6);
-      q += 2;
-      strcpy(q,"\\\\");
-      q += 2;
-      StrnCpy(q,myname,16);
-      strupper(q);
-      q = skip_string(q,1);
-      SSVAL(q,0,0xFFFF);
-      q += 2;
-
-      DEBUG(3,("Domain login request from %s(%s) user=%s\n",
-              machine,inet_ntoa(p->ip),user));
+      if (!group && !ip_equal(ip,n->ip) && question->name_type != 0x3)
+       {
+         if (n->source == SELF)
+           {
+             rcode = 6;
+             success = False;
+           }
+         else
+           {
+             n->ip = ip;
+             n->death_time = ttl?p->timestamp+ttl*3:0;
+             DEBUG(3,("%s changed owner to %s\n",
+                      namestr(&n->name),inet_ntoa(n->ip)));
+           }
+       }
+      else
+       {
+         /* refresh the name */
+         if (n->source != SELF)
+           {
+             n->death_time = ttl?p->timestamp + ttl*3:0;
+           }
+       }
     }
-    break;
-  case 7:    
+  else
     {
-      char *machine = buf+2;
-      logname = skip_string(machine,1);
-
-      SSVAL(q,0,0xc);
-      q += 2;
-      StrnCpy(q,domain_controller(),16);
-      strupper(q);
-      q = skip_string(q,1);
-      q += PutUniCode(q,domain_controller());
-      q += PutUniCode(q,dgram->dest_name.name);
-      SSVAL(q,0,0xFFFF);
-      q += 2;
-
-      DEBUG(3,("GETDC request from %s(%s)\n",
-              machine,inet_ntoa(p->ip)));
+      /* add the name to our subnet/name database */
+      n = add_netbios_entry(qname,name_type,nb_flags,ttl,REGISTER,ip);
     }
-    break;
-  default:
-    DEBUG(3,("Unknown domain request %d\n",code));
-    return;
-  }
-
-
-  send_mailslot_reply(logname,ClientDGRAM,outbuf,PTR_DIFF(q,outbuf),
-                     myname,&dgram->source_name.name[0],0,0,p->ip,myip);  
-}
-
-/****************************************************************************
-process udp 138 datagrams
-****************************************************************************/
-static void process_dgram(struct packet_struct *p)
-{
-  char *buf;
-  char *buf2;
-  int len;
-  struct dgram_packet *dgram = &p->packet.dgram;
-
-  if (dgram->header.msg_type != 0x10 &&
-      dgram->header.msg_type != 0x11 &&
-      dgram->header.msg_type != 0x12) {
-    /* don't process error packets etc yet */
-    return;
-  }
-
-  buf = &dgram->data[0];
-  buf -= 4; /* XXXX for the pseudo tcp length - 
-              someday I need to get rid of this */
-
-  if (CVAL(buf,smb_com) != SMBtrans) return;
-
-  len = SVAL(buf,smb_vwv11);
-  buf2 = smb_base(buf) + SVAL(buf,smb_vwv12);
-
-  DEBUG(3,("datagram from %s to %s for %s of type %d len=%d\n",
-          namestr(&dgram->source_name),namestr(&dgram->dest_name),
-          smb_buf(buf),CVAL(buf2,0),len));
-
-  if (len <= 0) return;
-
-  if (strequal(smb_buf(buf),"\\MAILSLOT\\BROWSE")) {
-    process_browse_packet(p,buf2,len);
-  } else if (strequal(smb_buf(buf),"\\MAILSLOT\\NET\\NETLOGON")) {
-    process_logon_packet(p,buf2,len);
-  }
-
-}
-
-/*******************************************************************
-  find a workgroup using the specified broadcast
-  ******************************************************************/
-static BOOL find_workgroup(char *name,struct in_addr ip)
-{
-  fstring name1;
-  BOOL ret;
-  struct in_addr ipout;
-
-  strcpy(name1,MSBROWSE);
-
-  ret = name_query(ClientNMB,name1,0x1,True,False,ip,&ipout,queue_packet);
-  if (!ret) return(False);
-
-  name_status(ClientNMB,name1,0x1,False,ipout,name,NULL,queue_packet);
-
-  if (name[0] != '*') {
-    DEBUG(2,("Found workgroup %s on broadcast %s\n",name,inet_ntoa(ip)));
-  } else {
-    DEBUG(3,("Failed to find workgroup %s on broadcast %s\n",name,inet_ntoa(ip)));
-  }
-  return(name[0] != '*');
-}
-
-
-/****************************************************************************
-  a hook for announce handling - called every minute
-  **************************************************************************/
-static void do_announcements(void)
-{
-  struct domain_record *d;
-
-  for (d = domainlist; d; d = d->next) {
-    /* if the ip address is 0 then set to the broadcast */
-    if (zero_ip(d->bcast_ip)) d->bcast_ip = bcast_ip;
-
-    /* if the workgroup is '*' then find a workgroup to be part of */
-    if (d->name[0] == '*') {
-      if (!find_workgroup(d->name,d->bcast_ip)) continue;
-      add_host_entry(d->name,0x1e,False,0,SELF,
-                    *interpret_addr2("255.255.255.255"));
-      if (!PrimaryGroup[0] && ip_equal(bcast_ip,d->bcast_ip)) {
-       strcpy(PrimaryGroup,d->name);
-       strupper(PrimaryGroup);
-      }
-    }
-
-    announce_host(d,myname,ServerComment);
-  }
-
-  /* if I have a domain controller then announce to it */
-  if (AM_MASTER)
-    announce_master(PrimaryGroup);
-
-  needannounce=False;
-}
-
-/*******************************************************************
-  check if someone still owns a name
-  ******************************************************************/
-static BOOL confirm_name(struct name_record *n)
-{
-  struct in_addr ipout;
-  BOOL ret = name_query(ClientNMB,n->name.name,
-                       n->name.name_type,False,
-                       False,n->ip,&ipout,queue_packet);
-  return(ret && ip_equal(ipout,n->ip));
-}
-
-/****************************************************************************
-reply to a name release
-****************************************************************************/
-static void reply_name_release(struct packet_struct *p)
-{
-  struct nmb_packet *nmb = &p->packet.nmb;
-  struct packet_struct p2;
-  struct nmb_packet *nmb2;
-  struct res_rec answer_rec;
-  struct in_addr ip;
-  int rcode=0;
-  int nb_flags = nmb->additional->rdata[0];
-  BOOL bcast = nmb->header.nm_flags.bcast;
   
-
-  putip((char *)&ip,&nmb->additional->rdata[2]);  
-
-  {
-    struct name_record *n = find_name(&nmb->question.question_name);
-    if (n && n->unique && n->source == REGISTER &&
-       ip_equal(ip,n->ip)) {
-      remove_name(n); n = NULL;
-    }
-
-    /* XXXX under what conditions should we reject the removal?? */
-  }
-
-  DEBUG(3,("Name release on name %s rcode=%d\n",
-          namestr(&nmb->question.question_name),rcode));
-
   if (bcast) return;
-
-  /* Send a NAME RELEASE RESPONSE */
-  p2 = *p;
-  nmb2 = &p2.packet.nmb;
-
-  nmb2->header.response = True;
-  nmb2->header.nm_flags.bcast = False;
-  nmb2->header.nm_flags.recursion_available = CanRecurse;
-  nmb2->header.nm_flags.trunc = False;
-  nmb2->header.nm_flags.authoritative = True; 
-  nmb2->header.qdcount = 0;
-  nmb2->header.ancount = 1;
-  nmb2->header.nscount = 0;
-  nmb2->header.arcount = 0;
-  nmb2->header.rcode = rcode;
-
-  nmb2->answers = &answer_rec;
-  bzero((char *)nmb2->answers,sizeof(*nmb2->answers));
   
-  nmb2->answers->rr_name = nmb->question.question_name;
-  nmb2->answers->rr_type = nmb->question.question_type;
-  nmb2->answers->rr_class = nmb->question.question_class;
-  nmb2->answers->ttl = 0; 
-  nmb2->answers->rdlength = 6;
-  nmb2->answers->rdata[0] = nb_flags;
-  putip(&nmb2->answers->rdata[2],(char *)&ip);
-
-  send_packet(&p2);
-}
-
-/****************************************************************************
-  reply to a reg request
-  **************************************************************************/
-static void reply_name_reg(struct packet_struct *p)
-{
-  struct nmb_packet *nmb = &p->packet.nmb;
-  char *qname = nmb->question.question_name.name;
-  BOOL wildcard = (qname[0] == '*'); 
-  BOOL bcast = nmb->header.nm_flags.bcast;
-  int ttl = GET_TTL(nmb->additional->ttl);
-  int name_type = nmb->question.question_name.name_type;
-  int nb_flags = nmb->additional->rdata[0];
-  struct packet_struct p2;
-  struct nmb_packet *nmb2;
-  struct res_rec answer_rec;
-  struct in_addr ip;
-  BOOL group = (nb_flags&0x80)?True:False;
-  int rcode = 0;  
-
-  if (wildcard) return;
-
-  putip((char *)&ip,&nmb->additional->rdata[2]);
-
-  if (group) {
-    /* apparently we should return 255.255.255.255 for group queries (email from MS) */
-    ip = *interpret_addr2("255.255.255.255");
-  }
-
+  update_from_reg(nmb->question.question_name.name,
+                 nmb->question.question_name.name_type, from_ip);
+  
+  /* XXXX don't know how to reject a name register: stick info in anyway
+     and guess that it doesn't matter if info is there! */
+  /*if (success)*/
   {
-    struct name_record *n = find_name(&nmb->question.question_name);
-
-    if (n) {
-      if (!group && !ip_equal(ip,n->ip)) {
-       /* check if the previous owner still wants it, 
-          if so reject the registration, otherwise change the owner 
-          and refresh */
-       if (n->source != REGISTER || confirm_name(n)) {
-         rcode = 6;
-       } else {
-         n->ip = ip;
-         n->death_time = ttl?p->timestamp+ttl*3:0;
-         DEBUG(3,("%s changed owner to %s\n",
-                  namestr(&n->name),inet_ntoa(n->ip)));
-       }
-      } else {
-       /* refresh the name */
-       if (n->source != SELF)
-         n->death_time = ttl?p->timestamp + ttl*3:0;
-      }
-    } else {
-      /* add the name to our database */
-      n = add_host_entry(qname,name_type,!group,ttl,REGISTER,ip);
-    }
+    rdata[0] = nb_flags;
+    rdata[1] = 0;
+    putip(&rdata[2],(char *)&ip);
   }
-
-  if (bcast) return;
-
-  DEBUG(3,("Name registration for name %s at %s rcode=%d\n",
-          namestr(&nmb->question.question_name),
-          inet_ntoa(ip),rcode));
-
-  /* Send a NAME REGISTRATION RESPONSE */
-  /* a lot of fields get copied from the query. This gives us the IP
-     and port the reply will be sent to etc */
-  p2 = *p;
-  nmb2 = &p2.packet.nmb;
-
-  nmb2->header.opcode = 5; 
-  nmb2->header.response = True;
-  nmb2->header.nm_flags.bcast = False;
-  nmb2->header.nm_flags.recursion_available = CanRecurse;
-  nmb2->header.nm_flags.trunc = False;
-  nmb2->header.nm_flags.authoritative = True; 
-  nmb2->header.qdcount = 0;
-  nmb2->header.ancount = 1;
-  nmb2->header.nscount = 0;
-  nmb2->header.arcount = 0;
-  nmb2->header.rcode = rcode;
-
-  nmb2->answers = &answer_rec;
-  bzero((char *)nmb2->answers,sizeof(*nmb2->answers));
   
-  nmb2->answers->rr_name = nmb->question.question_name;
-  nmb2->answers->rr_type = nmb->question.question_type;
-  nmb2->answers->rr_class = nmb->question.question_class;
-
-  nmb2->answers->ttl = ttl; 
-  nmb2->answers->rdlength = 6;
-  nmb2->answers->rdata[0] = nb_flags;
-  putip(&nmb2->answers->rdata[2],(char *)&ip);
-
-  send_packet(&p2);  
+  /* Send a NAME REGISTRATION RESPONSE */
+  reply_netbios_packet(p,nmb->header.name_trn_id,rcode,opcode,
+                      &nmb->question.question_name,
+                      nmb->question.question_type,
+                      nmb->question.question_class,
+                      ttl,
+                      rdata, 6 /*success ? 6 : 0*/);
 }
 
 
 /****************************************************************************
 reply to a name status query
 ****************************************************************************/
-static void reply_name_status(struct packet_struct *p)
+void reply_name_status(struct packet_struct *p)
 {
   struct nmb_packet *nmb = &p->packet.nmb;
-  char *qname = nmb->question.question_name.name;
+  char *qname   = nmb->question.question_name.name;
+  int ques_type = nmb->question.question_name.name_type;
   BOOL wildcard = (qname[0] == '*'); 
-  struct packet_struct p2;
-  struct nmb_packet *nmb2;
-  struct res_rec answer_rec;
-  char *buf;
-  int count;
-  int rcode = 0;
-  struct name_record *n = find_name(&nmb->question.question_name);
-
-  DEBUG(3,("Name status for name %s\n",
-          namestr(&nmb->question.question_name)));
-
-  if (!wildcard && (!n || n->source != SELF)) 
-    return;
-
-  /* Send a POSITIVE NAME STATUS RESPONSE */
-  /* a lot of fields get copied from the query. This gives us the IP
-     and port the reply will be sent to etc */
-  p2 = *p;
-  nmb2 = &p2.packet.nmb;
-
-  nmb2->header.response = True;
-  nmb2->header.nm_flags.bcast = False;
-  nmb2->header.nm_flags.recursion_available = CanRecurse;
-  nmb2->header.nm_flags.trunc = False;
-  nmb2->header.nm_flags.authoritative = True; /* WfWg ignores 
-                                                non-authoritative answers */
-  nmb2->header.qdcount = 0;
-  nmb2->header.ancount = 1;
-  nmb2->header.nscount = 0;
-  nmb2->header.arcount = 0;
-  nmb2->header.rcode = rcode;
-
-  nmb2->answers = &answer_rec;
-  bzero((char *)nmb2->answers,sizeof(*nmb2->answers));
+  char rdata[MAX_DGRAM_SIZE];
+  char *countptr, *buf;
+  int count, names_added;
+  struct name_record *n;
   
-
-  nmb2->answers->rr_name = nmb->question.question_name;
-  nmb2->answers->rr_type = nmb->question.question_type;
-  nmb2->answers->rr_class = nmb->question.question_class;
-  nmb2->answers->ttl = 0; 
-
-  for (count=0, n = namelist ; n; n = n->next) {
-    if (n->source != SELF) continue;
-    count++;
-  }
-
-  count = MIN(count,400/18); /* XXXX hack, we should calculate exactly
-                               how many will fit */
-
+  DEBUG(3,("Name status for name %s %s\n",
+          namestr(&nmb->question.question_name), inet_ntoa(p->ip)));
+  
+  /* find a name: if it's a wildcard, search the entire database.
+     if not, search for source SELF names only */
+  n = find_name_search(&nmb->question.question_name,
+                      wildcard ? FIND_GLOBAL : FIND_SELF, p->ip);
+  
+  if (!wildcard && (!n || n->source != SELF)) return;
+  
+  for (count=0, n = namelist ; n; n = n->next)
+    {
+      int name_type = n->name.name_type;
+      
+      if (n->source != SELF) continue;
+      
+      if (name_type >= 0x1b && name_type <= 0x20 && 
+         ques_type >= 0x1b && ques_type <= 0x20)
+       {
+         if (!strequal(qname, n->name.name)) continue;
+       }
+      
+      count++;
+    }
+  
+  /* XXXX hack, we should calculate exactly how many will fit */
+  count = MIN(count,(sizeof(rdata) - 64) / 18);
   
-  buf = &nmb2->answers->rdata[0];
-  SCVAL(buf,0,count);
+  countptr = buf = rdata;
   buf += 1;
-
-  for (n = namelist ; n; n = n->next) 
+  
+  names_added = 0;
+  
+  for (n = namelist ; n && count >= 0; n = n->next) 
     {
+      int name_type = n->name.name_type;
+      
       if (n->source != SELF) continue;
-
+      
+      /* start with first bit of putting info in buffer: the name */
+      
       bzero(buf,18);
-      strcpy(buf,n->name.name);
+      StrnCpy(buf,n->name.name,15);
       strupper(buf);
-      buf[15] = n->name.name_type;
-      buf += 16;
-      buf[0] = 0x4; /* active */
-      if (!n->unique) buf[0] |= 0x80; /* group */
-      buf += 2;
+      
+      /* now check if we want to exclude other workgroup names
+        from the response. if we don't exclude them, windows clients
+        get confused and will respond with an error for NET VIEW */
+      
+      if (name_type >= 0x1b && name_type <= 0x20 && 
+         ques_type >= 0x1b && ques_type <= 0x20)
+       {
+         if (!strequal(qname, n->name.name)) continue;
+       }
+      
+      /* carry on putting name info in buffer */
+      
+      buf[15] = name_type;
+      buf[16]  = n->nb_flags;
+      
+      buf += 18;
+      
       count--;
+      names_added++;
     }
-
+  
+  if (count < 0)
+    {
+      DEBUG(3, (("too many names: missing a few!\n")));
+    }
+  
+  SCVAL(countptr,0,names_added);
+  
   /* XXXXXXX we should fill in more fields of the statistics structure */
   bzero(buf,64);
   {
@@ -1722,597 +612,434 @@ static void reply_name_status(struct packet_struct *p)
     SIVAL(buf,20,num_good_sends);
     SIVAL(buf,24,num_good_receives);
   }
+  
   SIVAL(buf,46,0xFFB8E5); /* undocumented - used by NT */
-
+  
   buf += 64;
+  
+  /* Send a POSITIVE NAME STATUS RESPONSE */
+  reply_netbios_packet(p,nmb->header.name_trn_id,0,0,
+                      &nmb->question.question_name,
+                      nmb->question.question_type,
+                      nmb->question.question_class,
+                      0,
+                      rdata,PTR_DIFF(buf,rdata));
+}
 
-  nmb2->answers->rdlength = PTR_DIFF(buf,&nmb2->answers->rdata[0]);
-
-  send_packet(&p2);
-}
-
-
-
-/****************************************************************************
-reply to a name query
-****************************************************************************/
-static void reply_name_query(struct packet_struct *p)
-{
-  struct nmb_packet *nmb = &p->packet.nmb;
-  char *qname = nmb->question.question_name.name;
-  BOOL wildcard = (qname[0] == '*'); 
-  BOOL bcast = nmb->header.nm_flags.bcast;
-  struct in_addr retip;
-  int name_type = nmb->question.question_name.name_type;
-  struct packet_struct p2;
-  struct nmb_packet *nmb2;
-  struct res_rec answer_rec;
-  int ttl=0;
-  int rcode=0;
-  BOOL unique = True;
-
-  DEBUG(3,("Name query for %s from %s (bcast=%s) - ",
-          namestr(&nmb->question.question_name),
-          inet_ntoa(p->ip),
-          BOOLSTR(bcast)));
-
-  if (wildcard)
-    retip = myip;
-
-  if (!wildcard) {
-    struct name_record *n = find_name(&nmb->question.question_name);
 
-    if (!n) {
-      struct in_addr ip;
+/***************************************************************************
+reply to a name query
+****************************************************************************/
+struct name_record *search_for_name(struct nmb_name *question,
+                                   struct in_addr ip, int Time, int search)
+{
+  int name_type = question->name_type;
+  char *qname = question->name;
+  BOOL dns_type = name_type == 0x20 || name_type == 0;
+  
+  struct name_record *n;
+  
+  DEBUG(3,("Search for %s from %s - ", namestr(question), inet_ntoa(ip)));
+  
+  /* first look up name in cache */
+  n = find_name_search(question,search,ip);
+  
+  /* now try DNS lookup. */
+  if (!n)
+    {
+      struct in_addr dns_ip;
       unsigned long a;
-
+      
       /* only do DNS lookups if the query is for type 0x20 or type 0x0 */
-      if (name_type != 0x20 && name_type != 0) {
-       DEBUG(3,("not found\n"));
-       return;
-      }
-
+      if (!dns_type)
+       {
+         DEBUG(3,("types 0x20 0x1b 0x0 only: name not found\n"));
+         return NULL;
+       }
+      
       /* look it up with DNS */      
       a = interpret_addr(qname);
-
-      putip((char *)&ip,(char *)&a);
-
-      if (!a) {
-       /* no luck with DNS. We could possibly recurse here XXXX */
-       /* if this isn't a bcast then we should send a negative reply XXXX */
-       DEBUG(3,("no recursion\n"));
-       add_host_entry(qname,name_type,True,60*60,DNSFAIL,ip);
-       return;
-      }
-
+      
+      putip((char *)&dns_ip,(char *)&a);
+      
+      if (!a)
+       {
+         /* no luck with DNS. We could possibly recurse here XXXX */
+         /* if this isn't a bcast then we should send a negative reply XXXX */
+         DEBUG(3,("no recursion\n"));
+         add_netbios_entry(qname,name_type,NB_ACTIVE,60*60,DNSFAIL,dns_ip);
+         return NULL;
+       }
+      
       /* add it to our cache of names. give it 2 hours in the cache */
-      n = add_host_entry(qname,name_type,True,2*60*60,DNS,ip);
-
+      n = add_netbios_entry(qname,name_type,NB_ACTIVE,2*60*60,DNS,dns_ip);
+      
       /* failed to add it? yikes! */
-      if (!n) return;
-    }
-
-    /* don't respond to bcast queries for group names unless we own them */
-    if (bcast && !n->unique && !n->source == SELF) {
-      DEBUG(3,("no bcast replies\n"));
-      return;
-    }
-
-    /* don't respond to bcast queries for addresses on the same net as the 
-       machine doing the querying unless its our IP */
-    if (bcast && 
-       n->source != SELF && 
-       same_net(n->ip,p->ip)) {
-      DEBUG(3,("same net\n"));      
-      return;
+      if (!n) return NULL;
     }
-
-    /* is our entry already dead? */
-    if (n->death_time) {
-      if (n->death_time < p->timestamp) return;
-      ttl = n->death_time - p->timestamp;
+  
+  /* is our entry already dead? */
+  if (n->death_time)
+    {
+      if (n->death_time < Time) return False;
     }
-
-    retip = n->ip;
-    unique = n->unique;
-
-    /* it may have been an earlier failure */
-    if (n->source == DNSFAIL) {
+  
+  /* it may have been an earlier failure */
+  if (n->source == DNSFAIL)
+    {
       DEBUG(3,("DNSFAIL\n"));
-      return;
+      return NULL;
     }
-  }
-
-  /* if the IP is 0 then substitute my IP - we should see which one is on the 
-     right interface for the caller to do this right XXX */
-  if (zero_ip(retip)) retip = myip;
   
-  DEBUG(3,("OK %s rcode=%d\n",inet_ntoa(retip),rcode));      
-
-  /* a lot of fields get copied from the query. This gives us the IP
-     and port the reply will be sent to etc */
-  p2 = *p;
-  nmb2 = &p2.packet.nmb;
-
-  nmb2->header.response = True;
-  nmb2->header.nm_flags.bcast = False;
-  nmb2->header.nm_flags.recursion_available = CanRecurse;
-  nmb2->header.nm_flags.trunc = False;
-  nmb2->header.nm_flags.authoritative = True; /* WfWg ignores 
-                                                non-authoritative answers */
-  nmb2->header.qdcount = 0;
-  nmb2->header.ancount = 1;
-  nmb2->header.nscount = 0;
-  nmb2->header.arcount = 0;
-  nmb2->header.rcode = rcode;
-
-  nmb2->answers = &answer_rec;
-  bzero((char *)nmb2->answers,sizeof(*nmb2->answers));
-
-  nmb2->answers->rr_name = nmb->question.question_name;
-  nmb2->answers->rr_type = nmb->question.question_type;
-  nmb2->answers->rr_class = nmb->question.question_class;
-  nmb2->answers->ttl = ttl;
-  nmb2->answers->rdlength = 6;
-  nmb2->answers->rdata[0] = unique?0:0x80; 
-  nmb2->answers->rdata[1] = 0; 
-  putip(&nmb2->answers->rdata[2],(char *)&retip);
-
-  send_packet(&p2);
+  DEBUG(3,("OK %s\n",inet_ntoa(n->ip)));      
+  
+  return n;
 }
 
 
+/***************************************************************************
+reply to a name query.
 
-/* the global packet linked-list. incoming entries are added to the
-   end of this list.  it is supposed to remain fairly short so we
-   won't bother with an end pointer. */
-static struct packet_struct *packet_queue = NULL;
+with broadcast name queries:
 
+       - only reply if the query is for one of YOUR names. all other machines on
+         the network will be doing the same thing (that is, only replying to a
+         broadcast query if they own it)
+         NOTE: broadcast name queries should only be sent out by a machine
+         if they HAVEN'T been configured to use WINS. this is generally bad news
+         in a wide area tcp/ip network and should be rectified by the systems
+         administrator. USE WINS! :-)
+       - the exception to this is if the query is for a Primary Domain Controller
+         type name (0x1b), in which case, a reply is sent.
 
-/*******************************************************************
-  queue a packet into the packet queue
-  ******************************************************************/
-static void queue_packet(struct packet_struct *packet)
-{
-  struct packet_struct *p;
-  if (!packet_queue) {
-    packet->prev = NULL;
-    packet->next = NULL;
-    packet_queue = packet;
-    return;
-  }
-  
-  /* find the bottom */
-  for (p=packet_queue;p->next;p=p->next) ;
+       - NEVER send a negative response to a broadcast query. no-one else will!
 
-  p->next = packet;
-  packet->next = NULL;
-  packet->prev = p;
-}
+with directed name queries:
 
-/****************************************************************************
-  process a nmb packet
-  ****************************************************************************/
-static void process_nmb(struct packet_struct *p)
+       - if you are the WINS server, you are expected to 
+****************************************************************************/
+extern void reply_name_query(struct packet_struct *p)
 {
   struct nmb_packet *nmb = &p->packet.nmb;
-
-  /* if this is a response then ignore it */
-  if (nmb->header.response) return;
-
-  switch (nmb->header.opcode) 
+  struct nmb_name *question = &nmb->question.question_name;
+  int name_type = question->name_type;
+  BOOL dns_type = name_type == 0x20 || name_type == 0;
+  BOOL bcast = nmb->header.nm_flags.bcast;
+  int ttl=0;
+  int rcode = 0;
+  int nb_flags = 0;
+  struct in_addr retip;
+  char rdata[6];
+  
+  struct in_addr gp_ip = *interpret_addr2("255.255.255.255");
+  BOOL success = True;
+  
+  struct name_record *n;
+  enum name_search search = dns_type || name_type == 0x1b ?
+    FIND_GLOBAL : FIND_SELF;
+  
+  DEBUG(3,("Name query "));
+  
+  if ((n = search_for_name(question,p->ip,p->timestamp, search)))
     {
-    case 5:
-    case 8:
-    case 9:
-      if (nmb->header.qdcount>0 && 
-         nmb->header.arcount>0) {
-       reply_name_reg(p);
-       return;
-      }
-      break;
-
-    case 0:
-      if (nmb->header.qdcount>0) 
+      /* don't respond to broadcast queries unless the query is for
+        a name we own or it is for a Primary Domain Controller name */
+      if (bcast && n->source != SELF && name_type != 0x1b)
        {
-         switch (nmb->question.question_type)
-           {
-           case 0x20:
-             reply_name_query(p);
-             break;
-
-           case 0x21:
-             reply_name_status(p);
-             break;
-           }
-         return;
+         if (!lp_wins_proxy() || same_net(p->ip,n->ip,Netmask)) {
+           /* never reply with a negative response to broadcast queries */
+           return;
+         }
        }
-      break;
-
-    case 6:
-      if (nmb->header.qdcount>0 && 
-         nmb->header.arcount>0) {
-       reply_name_release(p);
-       return;
-      }
-      break;
+      
+      /* we will reply */
+      ttl = n->death_time - p->timestamp;
+      retip = n->ip;
+      nb_flags = n->nb_flags;
     }
-
-}
-
-
-
-/*******************************************************************
-  run elements off the packet queue till its empty
-  ******************************************************************/
-static void run_packet_queue(void)
-{
-  struct packet_struct *p;
-
-  while ((p=packet_queue)) {
-    switch (p->packet_type)
-      {
-      case NMB_PACKET:
-       process_nmb(p);
-       break;
-
-      case DGRAM_PACKET:
-       process_dgram(p);
-       break;
-      }
-
-    packet_queue = packet_queue->next;
-    if (packet_queue) packet_queue->prev = NULL;
-    free_packet(p);
-  }
-}
-
-
-/****************************************************************************
-  The main select loop, listen for packets and respond
-  ***************************************************************************/
-void process(void)
-{
-
-  while (True)
+  else
     {
-      fd_set fds;
-      int selrtn;
-      struct timeval timeout;
-
-      if (needelection && PrimaryGroup[0] && !RunningElection) {
-       DEBUG(3,(">>> Starting election on %s <<<\n",PrimaryGroup));
-       ElectionCount = 0;
-       RunningElection = True;
-       needelection = False;
-      }
-
-      FD_ZERO(&fds);
-      FD_SET(ClientNMB,&fds);
-      FD_SET(ClientDGRAM,&fds);
-      /* during elections we need to send election packets at one
-         second intervals */
-      timeout.tv_sec = RunningElection?1:NMBD_SELECT_LOOP;
-      timeout.tv_usec = 0;
-
-      selrtn = sys_select(&fds,&timeout);
-
-      if (FD_ISSET(ClientNMB,&fds)) {
-       struct packet_struct *packet = read_packet(ClientNMB,NMB_PACKET);
-       if (packet) queue_packet(packet);
-      }
-
-      if (FD_ISSET(ClientDGRAM,&fds)) {
-       struct packet_struct *packet = read_packet(ClientDGRAM,DGRAM_PACKET);
-       if (packet) queue_packet(packet);
-      }
-
-      if (RunningElection) 
-       run_election();
-
-      run_packet_queue();
-
-      do_announcements();
-
-      housekeeping();
+      if (bcast) return; /* never reply negative response to bcasts */
+      success = False;
     }
-}
+  
+  /* if asking for a group name (type 0x1e) return 255.255.255.255 */
+  if (ip_equal(retip, gp_ip) && name_type == 0x1e) retip = gp_ip;
 
+  /* if the IP is 0 then substitute my IP - we should see which one is on the 
+     right interface for the caller to do this right XXX */
+  if (zero_ip(retip)) retip = myip;
 
-/****************************************************************************
-  open the socket communication
-****************************************************************************/
-static BOOL open_sockets(BOOL isdaemon,int port)
-{
-  struct hostent *hp;
-  /* get host info */
-  if ((hp = Get_Hostbyname(myhostname)) == 0) 
+  if (success)
     {
-      DEBUG(0,( "Get_Hostbyname: Unknown host. %s\n",myhostname));
-      return False;
-    }   
-
-  if (isdaemon)
-    ClientNMB = open_socket_in(SOCK_DGRAM, port,0);
+      rcode = 0;
+      DEBUG(3,("OK %s\n",inet_ntoa(retip)));      
+    }
   else
-    ClientNMB = 0;
-
-  ClientDGRAM = open_socket_in(SOCK_DGRAM,DGRAM_PORT,3);
-
-  if (ClientNMB == -1)
-    return(False);
-
-  signal(SIGPIPE, SIGNAL_CAST sig_pipe);
-
-  set_socket_options(ClientNMB,"SO_BROADCAST");
-  set_socket_options(ClientDGRAM,"SO_BROADCAST");
-
-  DEBUG(3, ("Socket opened.\n"));
-  return True;
+    {
+      rcode = 3;
+      DEBUG(3,("UNKNOWN\n"));      
+    }
+  
+  if (success)
+    {
+      rdata[0] = nb_flags;
+      rdata[1] = 0;
+      putip(&rdata[2],(char *)&retip);
+    }
+  
+  reply_netbios_packet(p,nmb->header.name_trn_id,rcode,0,
+                      &nmb->question.question_name,
+                      nmb->question.question_type,
+                      nmb->question.question_class,
+                      ttl,
+                      rdata, success ? 6 : 0);
 }
 
 
-/*******************************************************************
-  check that a IP, bcast and netmask and consistent. Must be a 1s
-  broadcast
-  ******************************************************************/
-static BOOL ip_consistent(struct in_addr ip,struct in_addr bcast,
-                         struct in_addr nmask)
-{
-  unsigned long a_ip,a_bcast,a_nmask;
-
-  a_ip = ntohl(ip.s_addr);
-  a_bcast = ntohl(bcast.s_addr);
-  a_nmask = ntohl(nmask.s_addr);
-
-  /* check the netmask is sane */
-  if (((a_nmask>>24)&0xFF) != 0xFF) {
-    DEBUG(0,("Insane netmask %s\n",inet_ntoa(nmask)));
-    return(False);
-  }
-
-  /* check the IP and bcast are on the same net */
-  if ((a_ip&a_nmask) != (a_bcast&a_nmask)) {
-    DEBUG(0,("IP and broadcast are on different nets!\n"));
-    return(False);
-  }
-
-  /* check the IP and bcast are on the same net */
-  if ((a_bcast|a_nmask) != 0xFFFFFFFF) {
-    DEBUG(0,("Not a ones based broadcast %s\n",inet_ntoa(bcast)));
-    return(False);
-  }
-
-  return(True);
-}
-
 /****************************************************************************
-  initialise connect, service and file structs
+response from a name query
 ****************************************************************************/
-static BOOL init_structs(void )
+static void response_netbios_packet(struct packet_struct *p)
 {
-  if (!get_myname(myhostname,got_myip?NULL:&myip))
-    return(False);
-
-  /* Read the broadcast address from the interface */
-  {
-    struct in_addr ip0,ip1,ip2;
-
-    ip0 = myip;
+  struct nmb_packet *nmb = &p->packet.nmb;
+  struct nmb_name *question = &nmb->question.question_name;
+  char *qname = question->name;
+  BOOL bcast = nmb->header.nm_flags.bcast;
+  struct name_response_record *n;
 
-    if (!(got_bcast && got_nmask))
-      {
-       get_broadcast(&ip0,&ip1,&ip2);
-
-       if (!got_myip)
-         myip = ip0;
-    
-       if (!got_bcast)
-         bcast_ip = ip1;
-    
-       if (!got_nmask)
-         Netmask = ip2;   
-      } 
-
-    DEBUG(1,("Using IP %s  ",inet_ntoa(myip))); 
-    DEBUG(1,("broadcast %s  ",inet_ntoa(bcast_ip)));
-    DEBUG(1,("netmask %s\n",inet_ntoa(Netmask)));    
-
-    if (!ip_consistent(myip,bcast_ip,Netmask)) {
-      DEBUG(0,("WARNING: The IP address, broadcast and Netmask are not consistent\n"));
-      DEBUG(0,("You are likely to experience problems with this setup!\n"));
+  if (nmb->answers == NULL)
+    {
+      DEBUG(3,("NMB packet response from %s (bcast=%s) - UNKNOWN\n",
+              inet_ntoa(p->ip),
+              BOOLSTR(bcast)));
+      return;
     }
+  
+  if (nmb->answers->rr_type == NMB_STATUS) {
+    DEBUG(3,("Name status "));
   }
 
-  if (! *myname) {
-    char *p;
-    strcpy(myname,myhostname);
-    p = strchr(myname,'.');
-    if (p) *p = 0;
+  if (nmb->answers->rr_type == NMB_QUERY)      {
+    DEBUG(3,("Name query "));
   }
 
-  {
-    extern fstring local_machine;
-    strcpy(local_machine,myname);
-    strupper(local_machine);
+  if (nmb->answers->rr_type == NMB_REG) {
+    DEBUG(3,("Name registration "));
   }
 
-  return True;
-}
-
-/****************************************************************************
-usage on the program
-****************************************************************************/
-static void usage(char *pname)
-{
-  DEBUG(0,("Incorrect program usage - is the command line correct?\n"));
-
-  printf("Usage: %s [-n name] [-B bcast address] [-D] [-p port] [-d debuglevel] [-l log basename]\n",pname);
-  printf("Version %s\n",VERSION);
-  printf("\t-D                    become a daemon\n");
-  printf("\t-p port               listen on the specified port\n");
-  printf("\t-d debuglevel         set the debuglevel\n");
-  printf("\t-l log basename.      Basename for log/debug files\n");
-  printf("\t-n netbiosname.       the netbios name to advertise for this host\n");
-  printf("\t-B broadcast address  the address to use for broadcasts\n");
-  printf("\t-N netmask           the netmask to use for subnet determination\n");
-  printf("\t-H hosts file        load a netbios hosts file\n");
-  printf("\t-I ip-address        override the IP address\n");
-  printf("\t-G group name        add a group name to be part of\n");
-  printf("\t-C comment           sets the machine comment that appears in browse lists\n");
-  printf("\n");
-}
-
-
-/****************************************************************************
-  main program
-  **************************************************************************/
-int main(int argc,char *argv[])
-{
-  int port = NMB_PORT;
-  int opt;
-  extern FILE *dbf;
-  extern char *optarg;
-
-  *host_file = 0;
-
-#if 0
-  sleep(10);
-#endif
-
-  StartupTime = time(NULL);
-
-  TimeInit();
-
-  strcpy(debugf,NMBLOGFILE);
-
-  setup_logging(argv[0],False);
+  if (nmb->answers->rr_type == NMB_REL) {
+    DEBUG(3,("Name release "));
+  }
 
-  charset_initialise();
+  DEBUG(3,("response for %s from %s (bcast=%s)\n",
+          namestr(&nmb->answers->rr_name),
+          inet_ntoa(p->ip),
+          BOOLSTR(bcast)));
+  
+  if (!(n = find_name_query(nmb->header.name_trn_id))) {
+    DEBUG(3,("unknown response (received too late or from nmblookup?)\n"));
+    return;
+  }
 
-#ifdef LMHOSTSFILE
-  strcpy(host_file,LMHOSTSFILE);
-#endif
+  n->num_msgs++; /* count number of responses received */
 
-  /* this is for people who can't start the program correctly */
-  while (argc > 1 && (*argv[1] != '-'))
+  switch (n->cmd_type)
     {
-      argv++;
-      argc--;
+    case MASTER_SERVER_CHECK     : DEBUG(4,("MASTER_SVR_CHECK\n")); break;
+    case SERVER_CHECK            : DEBUG(4,("SERVER_CHECK\n")); break;
+    case FIND_MASTER             : DEBUG(4,("FIND_MASTER\n")); break;
+    case NAME_STATUS_MASTER_CHECK: DEBUG(4,("NAME_STAT_MST_CHK\n")); break;
+    case NAME_STATUS_CHECK       : DEBUG(4,("NAME_STATUS_CHECK\n")); break;
+    case CHECK_MASTER            : DEBUG(4,("CHECK_MASTER\n")); break;
+    case NAME_CONFIRM_QUERY      : DEBUG(4,("NAME_CONFIRM_QUERY\n")); break;
+    default: break;
     }
-
-  fault_setup(fault_continue);
-
-  signal(SIGHUP,SIGNAL_CAST sig_hup);
-
-  bcast_ip = *interpret_addr2("0.0.0.0");
-  myip = *interpret_addr2("0.0.0.0");
-
-  while ((opt = getopt (argc, argv, "s:T:I:C:bAi:B:N:Rn:l:d:Dp:hSH:G:")) != EOF)
-    switch (opt)
+  switch (n->cmd_type)
+    {
+    case MASTER_SERVER_CHECK:
+    case SERVER_CHECK:
+    case FIND_MASTER:
       {
-      case 's':
-       strcpy(servicesf,optarg);
-       break;
-      case 'C':
-       strcpy(ServerComment,optarg);
-       break;
-      case 'G':
-       add_domain_entry(optarg,bcast_ip);
-       break;
-      case 'H':
-       strcpy(host_file,optarg);
-       break;
-      case 'I':
-       myip = *interpret_addr2(optarg);
-       got_myip = True;
-       break;
-      case 'B':
-       bcast_ip = *interpret_addr2(optarg);
-       got_bcast = True;
-       break;
-      case 'N':
-       Netmask = *interpret_addr2(optarg);
-       got_nmask = True;
-       break;
-      case 'n':
-       strcpy(myname,optarg);
-       break;
-      case 'l':
-       sprintf(debugf,"%s.nmb",optarg);
-       break;
-      case 'i':
-       strcpy(scope,optarg);
-       strupper(scope);
-       break;
-      case 'D':
-       is_daemon = True;
+       if (nmb->answers->rr_type == NMB_QUERY)
+         {
+           enum cmd_type cmd = (n->cmd_type == MASTER_SERVER_CHECK) ?
+             NAME_STATUS_MASTER_CHECK :
+             NAME_STATUS_CHECK;
+           if (n->num_msgs > 1 && !strequal(qname,n->name.name))
+             {
+               /* one subnet, one master browser per workgroup */
+               /* XXXX force an election? */
+               DEBUG(1,("more than one master browser replied!\n"));
+             }
+           
+           /* initiate a name status check on the server that replied */
+           queue_netbios_packet(ClientNMB,NMB_STATUS, cmd,
+                                nmb->answers->rr_name.name,
+                                nmb->answers->rr_name.name_type,0,
+                                False,False,n->to_ip);
+         }
+       else
+         {
+           DEBUG(1,("Name query reply has wrong answer rr_type\n"));
+         }
        break;
-      case 'd':
-       DEBUGLEVEL = atoi(optarg);
+      }
+      
+    case NAME_STATUS_MASTER_CHECK:
+    case NAME_STATUS_CHECK:
+      {
+       if (nmb->answers->rr_type == NMB_STATUS)
+         {
+           /* NMB_STATUS arrives: contains the workgroup name 
+              and server name we require */
+           struct nmb_name name;
+           fstring serv_name;
+           
+           if (interpret_node_status(nmb->answers->rdata,
+                                     &name,0x1d,serv_name,n->to_ip))
+             {
+               if (*serv_name)
+                 {
+                   sync_server(n->cmd_type,serv_name,
+                               name.name,name.name_type,
+                               n->to_ip);
+                 }
+             }
+           else
+             {
+               DEBUG(1,("No 0x1d name type in interpret_node_status()\n"));
+             }
+         }
+       else
+         {
+           DEBUG(1,("Name status reply has wrong answer rr_type\n"));
+         }
        break;
-      case 'p':
-       port = atoi(optarg);
+      }
+      
+    case CHECK_MASTER:
+      {
+       /* no action required here. it's when NO responses are received
+          that we need to do something (see expire_name_query_entries) */
+       
+       DEBUG(4, ("Master browser exists for %s at %s\n",
+                 namestr(&n->name),
+                 inet_ntoa(n->to_ip)));
+       if (n->num_msgs > 1)
+         {
+           DEBUG(1,("more than one master browser!\n"));
+         }
+       if (nmb->answers->rr_type != NMB_QUERY)
+         {
+           DEBUG(1,("Name query reply has wrong answer rr_type\n"));
+         }
        break;
-      case 'h':
-       usage(argv[0]);
-       exit(0);
+      }
+    case NAME_CONFIRM_QUERY:
+      {
+       DEBUG(4, ("Name query at WINS server: %s at %s - ",
+                 namestr(&n->name),
+                 inet_ntoa(n->to_ip)));
+       if (nmb->header.rcode == 0 && nmb->answers->rdata)
+         {
+           int nb_flags = nmb->answers->rdata[0];
+           struct in_addr found_ip;
+           putip((char*)&found_ip,&nmb->answers->rdata[2]);
+           
+           DEBUG(4, (" OK: %s\n", inet_ntoa(found_ip)));
+           add_netbios_entry(nmb->answers->rr_name.name,
+                             nmb->answers->rr_name.name_type,
+                             nb_flags,GET_TTL(0),STATUS_QUERY,found_ip);
+         }
+       else
+         {
+           DEBUG(4, (" NEGATIVE RESPONSE\n"));
+         }
+       
        break;
-      default:
-       if (!is_a_socket(0))
-         usage(argv[0]);
+      }
+    default:
+      {
+       DEBUG(0,("unknown command received in response_netbios_packet\n"));
        break;
       }
-  
-  DEBUG(1,("%s netbios nameserver version %s started\n",timestring(),VERSION));
-  DEBUG(1,("Copyright Andrew Tridgell 1994\n"));
-
-  init_structs();
-
-  if (!reload_services(False))
-    return(-1);        
-
-  if (*host_file)
-    {
-      load_hosts_file(host_file);
-      DEBUG(3,("Loaded hosts file\n"));
     }
+}
 
-  if (!*ServerComment)
-    strcpy(ServerComment,"Samba %v");
-  string_sub(ServerComment,"%v",VERSION);
-  string_sub(ServerComment,"%h",myhostname);
-
-  add_my_names();
-
-  DEBUG(3,("Checked names\n"));
-  
-  dump_names();
-
-  DEBUG(3,("Dumped names\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) {
-    DEBUG(2,("%s becoming a daemon\n",timestring()));
-    become_daemon();
-  }
 
+/****************************************************************************
+  process a nmb packet
+  ****************************************************************************/
+void process_nmb(struct packet_struct *p)
+{
+       struct nmb_packet *nmb = &p->packet.nmb;
 
-  DEBUG(3,("Opening sockets\n"));
+       debug_nmb_packet(p);
 
-  if (open_sockets(is_daemon,port))
-    {
-      process();
-      close_sockets();
-    }
+       switch (nmb->header.opcode) 
+       {
+               case 5:
+               case 8:
+               case 9:
+               {
+                       if (nmb->header.qdcount==0 || nmb->header.arcount==0) break;
+                       if (nmb->header.response)
+                               response_name_reg(p);
+                       else
+                               reply_name_reg(p);
+                       break;
+               }
+
+               case 0:
+               {
+                       if (nmb->header.response)
+                       {
+                               switch (nmb->question.question_type)
+                               {
+                                       case 0x0:
+                                       {
+                                               response_netbios_packet(p);
+                                               break;
+                                       }
+                               }
+                               return;
+                       }
+                       else if (nmb->header.qdcount>0) 
+                       {
+                               switch (nmb->question.question_type)
+                               {
+                                       case NMB_QUERY:
+                                       {
+                                               reply_name_query(p);
+                                               break;
+                                       }
+                                       case NMB_STATUS:
+                                       {
+                                               reply_name_status(p);
+                                               break;
+                                       }
+                               }
+                               return;
+                       }
+                       break;
+               }
+
+               case 6:
+               {
+                       if (nmb->header.qdcount==0 || nmb->header.arcount==0)
+                       {
+                               DEBUG(2,("netbios release packet rejected\n"));
+                               break;
+                       }
+
+                       if (nmb->header.response)
+                               response_name_release(p);
+                       else
+                               reply_name_release(p);
+                       break;
+               }
+       }
 
-  if (dbf)
-    fclose(dbf);
-  return(0);
 }
+