includes.h: Added USE_SETSID for SGI.
[kai/samba.git] / source / nameelect.c
index 8c93de03a8510eeb68f3440769fdfad63738b95b..258ee98931e0f526945cc657031738f2288768a2 100644 (file)
@@ -2,7 +2,7 @@
    Unix SMB/Netbios implementation.
    Version 1.9.
    NBT netbios routines and daemon - version 2
-   Copyright (C) Andrew Tridgell 1994-1996
+   Copyright (C) Andrew Tridgell 1994-1997
    
    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
@@ -40,11 +40,9 @@ extern int DEBUGLEVEL;
 extern pstring scope;
 
 extern pstring myname;
+extern fstring myworkgroup;
 extern struct in_addr ipzero;
-extern struct in_addr ipgrp;
-
-/* machine comment for host announcements */
-extern  pstring ServerComment;
+extern struct in_addr wins_ip;
 
 /* here are my election parameters */
 
@@ -54,44 +52,65 @@ extern struct subnet_record *subnetlist;
 
 extern uint16 nb_type; /* samba's NetBIOS name type */
 
+
 /*******************************************************************
   occasionally check to see if the master browser is around
   ******************************************************************/
-void check_master_browser(void)
+void check_master_browser(time_t t)
 {
   static time_t lastrun=0;
-  time_t t = time(NULL);
   struct subnet_record *d;
 
   if (!lastrun) lastrun = t;
-  if (t < lastrun + CHECK_TIME_MST_BROWSE * 60)
-    return;
+  if (t < lastrun + CHECK_TIME_MST_BROWSE * 60) return;
+
   lastrun = t;
 
   dump_workgroups();
 
-  for (d = subnetlist; d; d = d->next)
+  for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d))
+  {
+    struct work_record *work;
+
+    for (work = d->workgrouplist; work; work = work->next)
     {
-      struct work_record *work;
+      if (strequal(work->work_group, myworkgroup) && !AM_MASTER(work))
+      {
+        if (lp_local_master())
+        {
+          /* potential master browser - not a master browser.  force
+             becoming a master browser, hence the log message.
+           */
 
-      for (work = d->workgrouplist; work; work = work->next)
-       {
-         /* if we are not the browse master of a workgroup, and we can't
-            find a browser on the subnet, do something about it. */
+          DEBUG(0,("%s potential master for %s %s - force election\n",
+                   timestring(), work->work_group,
+                   inet_ntoa(d->bcast_ip)));
 
-         if (!AM_MASTER(work))
-           {
-             queue_netbios_packet(d,ClientNMB,NMB_QUERY,NAME_QUERY_MST_CHK,
-                                  work->work_group,0x1d,0,0,0,NULL,NULL,
-                                  True,False,d->bcast_ip,d->bcast_ip);
-           }
-       }
+          browser_gone(work->work_group, d->bcast_ip);
+        }
+        else
+        {
+          /* if we are not the browse master of a workgroup,
+             and we can't find a browser on the subnet, do
+             something about it.
+           */
+
+          queue_netbios_packet(d,ClientNMB,NMB_QUERY,NAME_QUERY_MST_CHK,
+                    work->work_group,0x1d,0,0,0,NULL,NULL,
+                    True,False,d->bcast_ip,d->bcast_ip);
+        }
+      }
     }
+  }
 }
 
 
 /*******************************************************************
-  what to do if a master browser DOESN't exist
+  what to do if a master browser DOESN't exist.
+
+  option 1: force an election, and participate in it
+  option 2: force an election, and let everyone else participate.
+
   ******************************************************************/
 void browser_gone(char *work_name, struct in_addr ip)
 {
@@ -100,25 +119,42 @@ void browser_gone(char *work_name, struct in_addr ip)
 
   /* i don't know about this workgroup, therefore i don't care */
   if (!work || !d) return;
-  if (strequal(work->work_group, lp_workgroup()))
+
+  /* don't do election stuff on the WINS subnet */
+  if (ip_equal(d->bcast_ip,wins_ip)) 
+    return;
+
+  if (strequal(work->work_group, myworkgroup))
   {
 
+    if (lp_local_master())
+    {
+      /* we have discovered that there is no local master
+         browser, and we are configured to initiate
+         an election under exactly such circumstances.
+       */
       DEBUG(2,("Forcing election on %s %s\n",
               work->work_group,inet_ntoa(d->bcast_ip)));
 
       /* we can attempt to become master browser */
       work->needelection = True;
-  }
-  else
-  {
-     /* local interfaces: force an election */
-    send_election(d, work->work_group, 0, 0, myname);
-
-     /* only removes workgroup completely on a local interface 
-        persistent lmhosts entries on a local interface _will_ be removed).
-      */
-     remove_workgroup(d, work,True);
+    }
+    else
+    {
+      /* we need to force an election, because we are configured
+         not to _become_ the local master, but we still _need_ one,
+         having detected that one doesn't exist.
+       */
+
+      /* local interfaces: force an election */
+      send_election(d, work->work_group, 0, 0, myname);
+
+      /* only removes workgroup completely on a local interface 
+         persistent lmhosts entries on a local interface _will_ be removed).
+       */
+      remove_workgroup(d, work,True);
+      add_workgroup_to_subnet(d, work->work_group);
+    }
   }
 }
 
@@ -150,7 +186,8 @@ void send_election(struct subnet_record *d, char *group,uint32 criterion,
   strupper(p);
   p = skip_string(p,1);
   
-  send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
+  send_mailslot_reply(False,BROWSE_MAILSLOT,ClientDGRAM,
+              outbuf,PTR_DIFF(p,outbuf),
                      name,group,0,0x1e,d->bcast_ip,*iface_ip(d->bcast_ip));
 }
 
@@ -166,26 +203,36 @@ void send_election(struct subnet_record *d, char *group,uint32 criterion,
 void name_unregister_work(struct subnet_record *d, char *name, int name_type)
 {
     struct work_record *work;
+    int remove_type_local  = 0;
+    int remove_type_domain = 0;
+    int remove_type_logon  = 0;
 
     remove_netbios_name(d,name,name_type,SELF,ipzero);
 
     if (!(work = find_workgroupstruct(d, name, False))) return;
 
-    if (ms_browser_name(name, name_type) ||
-        (AM_MASTER(work) && strequal(name, lp_workgroup()) == 0 &&
-         (name_type == 0x1d || name_type == 0x1b)))
+    /* work out what to unbecome, from the name type being removed */
+
+    if (ms_browser_name(name, name_type))
+    {
+      remove_type_local |= SV_TYPE_MASTER_BROWSER;
+    }
+    if (AM_MASTER(work) && strequal(name, myworkgroup) && name_type == 0x1d)
+    {
+      remove_type_local |= SV_TYPE_MASTER_BROWSER;
+    }
+    if (AM_DOMMST(work) && strequal(name, myworkgroup) && name_type == 0x1b)
     {
-      int remove_type = 0;
-
-      if (ms_browser_name(name, name_type))
-        remove_type = SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER;
-      if (name_type == 0x1d)
-        remove_type = SV_TYPE_MASTER_BROWSER;
-      if (name_type == 0x1b)
-        remove_type = SV_TYPE_DOMAIN_MASTER;
-                       
-      become_nonmaster(d, work, remove_type);
+      remove_type_domain |= SV_TYPE_DOMAIN_MASTER;
     }
+    if (AM_DOMMEM(work) && strequal(name, myworkgroup) && name_type == 0x1c)
+    {
+      remove_type_logon|= SV_TYPE_DOMAIN_MEMBER;
+    }
+
+    if (remove_type_local ) unbecome_local_master (d, work, remove_type_local );
+    if (remove_type_domain) unbecome_domain_master(d, work, remove_type_domain);
+    if (remove_type_logon ) unbecome_logon_server (d, work, remove_type_logon );
 }
 
 
@@ -200,218 +247,318 @@ void name_register_work(struct subnet_record *d, char *name, int name_type,
                                int nb_flags, time_t ttl, struct in_addr ip, BOOL bcast)
 {
   enum name_source source = (ismyip(ip) || ip_equal(ip, ipzero)) ?
-                                                               SELF : REGISTER;
+                                 SELF : REGISTER;
 
   if (source == SELF)
-  {
-    struct work_record *work = find_workgroupstruct(d, lp_workgroup(), False);
+    {
+      struct work_record *work = find_workgroupstruct(d, 
+                                  myworkgroup, False);
 
-    add_netbios_entry(d,name,name_type,nb_flags,ttl,source,ip,True,!bcast);
+      add_netbios_entry(d,name,name_type,nb_flags,ttl,source,ip,True,!bcast);
 
-    if (work)
-    {
-      if (work->state != MST_NONE)
+      if (work)
       {
-        /* samba is in the process of working towards master browser-ness.
-           initiate the next stage.
-         */
-        become_master(d, work);
-        return;
+        int add_type_local  = False;
+        int add_type_domain = False;
+        int add_type_logon  = False;
+
+        DEBUG(4,("checking next stage: name_register_work %s\n", name));
+
+        /* work out what to become, from the name type being added */
+
+        if (ms_browser_name(name, name_type))
+        {
+          add_type_local = True;
+        }
+        if (strequal(name, myworkgroup) && name_type == 0x1d)
+        {
+          add_type_local = True;
+        }
+        if (strequal(name, myworkgroup) && name_type == 0x1b)
+        {
+          add_type_domain = True;
+        }
+        if (strequal(name, myworkgroup) && name_type == 0x1c)
+        {
+          add_type_logon = True;
+        }
+
+        if (add_type_local ) become_local_master (d, work);
+        if (add_type_domain) become_domain_master(d, work);
+        if (add_type_logon ) become_logon_server (d, work);
       }
     }
-  }
 }
 
 
 /*******************************************************************
-  become the master browser.
+  become the local master browser.
 
   this is done in stages. note that this could take a while, 
   particularly on a broadcast subnet, as we have to wait for
   the implicit registration of each name to be accepted.
 
-  as each name is successfully registered, become_master() is
+  as each name is successfully registered, become_local_master() is
   called again, in order to initiate the next stage. see
   dead_netbios_entry() - deals with implicit name registration
   and response_name_reg() - deals with explicit registration
   with a WINS server.
 
-  stage 1: was MST_NONE - go to MST_NONE and register ^1^2__MSBROWSE__^2^1.
-  stage 2: was MST_WON  - go to MST_MSB  and register WORKGROUP(0x1d)
-  stage 3: was MST_MSB  - go to MST_BROWSER and register WORKGROUP(0x1b)
-  stage 4: was MST_BROWSER - go to MST_DOMAIN (do not pass GO, do not...)
+  stage 1: was MST_POTENTIAL - go to MST_POTENTIAL and register ^1^2__MSBROWSE__^2^1.
+  stage 2: was MST_BACK  - go to MST_MSB  and register WORKGROUP(0x1d)
+  stage 3: was MST_MSB  - go to MST_BROWSER and stay there 
 
   XXXX note: this code still does not cope with the distinction
   between different types of nodes, particularly between M and P
   nodes. that comes later.
 
   ******************************************************************/
-void become_master(struct subnet_record *d, struct work_record *work)
+void become_local_master(struct subnet_record *d, struct work_record *work)
 {
-  uint32 domain_type = SV_TYPE_DOMAIN_ENUM|SV_TYPE_SERVER_UNIX|0x00400000;
-
-  if (!work) return;
+  /* domain type must be limited to domain enum + server type. it must
+     not have SV_TYPE_SERVER or anything else with SERVER in it, else
+     clients get confused and start thinking this entry is a server
+     not a workgroup
+   */
+  uint32 domain_type = SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT;
+
+  if (!work || !d) 
+    return;
   
+  if (!lp_local_master())
+  { 
+    DEBUG(0,("Samba not configured as a local master browser.\n"));
+    return;
+  }
+
   DEBUG(2,("Becoming master for %s %s (currently at stage %d)\n",
-                                       work->work_group,inet_ntoa(d->bcast_ip),work->state));
+           work->work_group,inet_ntoa(d->bcast_ip),work->mst_state));
   
-  switch (work->state)
+  switch (work->mst_state)
   {
-    case MST_NONE: /* while we were nothing but a server... */
+    case MST_POTENTIAL: /* while we were nothing but a server... */
     {
       DEBUG(3,("go to first stage: register ^1^2__MSBROWSE__^2^1\n"));
-      work->state = MST_WON; /* ... an election win was successful */
+      work->mst_state = MST_BACK; /* an election win was successful */
 
       work->ElectionCriterion |= 0x5;
 
       /* update our server status */
       work->ServerType &= ~SV_TYPE_POTENTIAL_BROWSER;
-      add_server_entry(d,work,myname,work->ServerType,0,ServerComment,True);
+      add_server_entry(d,work,myname,work->ServerType,0,lp_serverstring(),True);
 
       /* add special browser name */
-      add_my_name_entry(d,MSBROWSE        ,0x01,nb_type|NB_ACTIVE|NB_GROUP);
+      add_my_name_entry(d,MSBROWSE,0x01,nb_type|NB_ACTIVE|NB_GROUP,False);
 
       /* DON'T do anything else after calling add_my_name_entry() */
-      return;
+      break;
     }
-    case MST_WON: /* while nothing had happened except we won an election... */
+
+    case MST_BACK: /* while nothing had happened except we won an election... */
     {
       DEBUG(3,("go to second stage: register as master browser\n"));
-      work->state = MST_MSB; /* ... registering MSBROWSE was successful */
+      work->mst_state = MST_MSB; /* registering MSBROWSE was successful */
 
       /* add server entry on successful registration of MSBROWSE */
       add_server_entry(d,work,work->work_group,domain_type,0,myname,True);
 
       /* add master name */
-      add_my_name_entry(d,work->work_group,0x1d,nb_type|NB_ACTIVE);
+      add_my_name_entry(d,work->work_group,0x1d,nb_type|NB_ACTIVE,False);
   
       /* DON'T do anything else after calling add_my_name_entry() */
-      return;
+      break;
     }
+
     case MST_MSB: /* while we were still only registered MSBROWSE state... */
     {
-      DEBUG(3,("2nd stage complete: registered as master browser\n"));
-      work->state = MST_BROWSER; /* ... registering WORKGROUP(1d) succeeded */
+      int i = 0;
+      struct server_record *sl;
+
+      DEBUG(3,("2nd stage complete: registered as master browser for workgroup %s \
+on subnet %s\n", work->work_group, inet_ntoa(d->bcast_ip)));
+      work->mst_state = MST_BROWSER; /* registering WORKGROUP(1d) succeeded */
 
       /* update our server status */
       work->ServerType |= SV_TYPE_MASTER_BROWSER;
-      add_server_entry(d,work,myname,work->ServerType,0,ServerComment,True);
 
-      if (work->serverlist == NULL) /* no servers! */
+      DEBUG(3,("become_local_master: updating our server %s to type %x\n", 
+                myname, work->ServerType));
+
+      add_server_entry(d,work,myname,work->ServerType,0,lp_serverstring(),True);
+
+      /* Count the number of servers we have on our list. If it's
+         less than 10 (just a heuristic) request the servers
+         to announce themselves.
+       */
+      for( sl = work->serverlist; sl != NULL; sl = sl->next)
+        i++;
+
+      if (i < 10)
       {
         /* ask all servers on our local net to announce to us */
         announce_request(work, d->bcast_ip);
       }
+
+      /* Reset the announce master timer so that we do an announce as soon as possible
+         now we are a master. */
+      reset_announce_timer();
+
+      DEBUG(0,("Samba is now a local master browser for workgroup %s on subnet %s\n", 
+                work->work_group, inet_ntoa(d->bcast_ip)));
+
       break;
-   }
+    }
 
-   case MST_BROWSER:
-   {
+    case MST_BROWSER:
+    {
       /* don't have to do anything: just report success */
       DEBUG(3,("3rd stage: become master browser!\n"));
-
       break;
-   }
+    }
+  }
+}
 
-   case MST_DOMAIN_NONE:
-   {
-      if (lp_domain_master())
-      {
-        work->state = MST_DOMAIN_MEM; /* ... become domain member */
-        DEBUG(3,("domain first stage: register as domain member\n"));
 
-        /* add domain member name */
-        add_my_name_entry(d,work->work_group,0x1e,nb_type|NB_ACTIVE|NB_GROUP);
+/*******************************************************************
+  become the domain master browser.
 
-        /* DON'T do anything else after calling add_my_name_entry() */
-        return;
-      }
-      else
-      {
-        DEBUG(4,("samba not configured as a domain master.\n"));
-      }
-  
-      break;
-   }
+  this is done in stages. note that this could take a while, 
+  particularly on a broadcast subnet, as we have to wait for
+  the implicit registration of each name to be accepted.
 
-   case MST_DOMAIN_MEM:
-   {
-      if (lp_domain_master())
-      {
-        work->state = MST_DOMAIN_TST; /* ... possibly become domain master */
-        DEBUG(3,("domain second stage: register as domain master\n"));
+  as each name is successfully registered, become_domain_master() is
+  called again, in order to initiate the next stage. see
+  dead_netbios_entry() - deals with implicit name registration
+  and response_name_reg() - deals with explicit registration
+  with a WINS server.
 
-        if (lp_domain_logons())
-           {
-          work->ServerType |= SV_TYPE_DOMAIN_MEMBER;
-          add_server_entry(d,work,myname,work->ServerType,0,ServerComment,True);
-        }
+  stage 1: was DOMAIN_NONE - go to DOMAIN_MST 
 
-        /* add domain master name */
-        add_my_name_entry(d,work->work_group,0x1b,nb_type|NB_ACTIVE         );
+  XXXX note: this code still does not cope with the distinction
+  between different types of nodes, particularly between M and P
+  nodes. that comes later.
 
-        /* DON'T do anything else after calling add_my_name_entry() */
-        return;
-      }
-      else
-      {
-        DEBUG(4,("samba not configured as a domain master.\n"));
-      }
+  ******************************************************************/
+void become_domain_master(struct subnet_record *d, struct work_record *work)
+{
+  /* domain type must be limited to domain enum + server type. it must
+     not have SV_TYPE_SERVER or anything else with SERVER in it, else
+     clients get confused and start thinking this entry is a server
+     not a workgroup
+   */
+
+  if (!work || !d) return;
+  if (!lp_domain_master())
+  { 
+    DEBUG(0,("Samba not configured as a domain master browser.\n"));
+    return;
+  }
+
+  DEBUG(2,("Becoming domain master for %s %s (currently at stage %d)\n",
+       work->work_group,inet_ntoa(d->bcast_ip),work->dom_state));
   
+  switch (work->dom_state)
+  {
+    case DOMAIN_NONE: /* while we were nothing but a server... */
+    {
+      DEBUG(3,("become_domain_master: go to first stage: register <1b> name\n"));
+      work->dom_state = DOMAIN_WAIT;
+
+      /* Registering the DOMAIN<1b> name is very tricky. We need to
+         do this on all our subnets, but don't want to bradcast it
+         on locally connected subnets (WinNT doesn't do this). Also,
+         previous versions of Samba screw up royally when we do this.
+         We need to register it immediatly on our local subnet, but
+         also actually check with the WINS server if it exists. If the name
+         has already been claimed by someone else in the WINS server
+         then we need to back out all our local registrations and
+         fail. Thus we only directly enter the name on local subnets,
+         on the WINS subnet we actually check...
+      */
+      /* XXXX the 0x1b is domain master browser name */
+      if(d == wins_subnet)
+        add_my_name_entry(d, work->work_group,0x1b,nb_type|NB_ACTIVE,False);
+      else
+        add_my_name_entry(d, work->work_group,0x1b,nb_type|NB_ACTIVE,True);
+
+      /* DON'T do anything else after calling add_my_name_entry() */
       break;
     }
 
-    case MST_DOMAIN_TST: /* while we were still a master browser... */
+    case DOMAIN_WAIT:
     {
+      work->dom_state = DOMAIN_MST; /* ... become domain master */
+      DEBUG(3,("become_domain_master: first stage - register as domain member\n"));
       /* update our server status */
-      if (lp_domain_master())
-      {
-        struct subnet_record *d1;
-               uint32 update_type = 0;
+      work->ServerType |= SV_TYPE_NT|SV_TYPE_DOMAIN_MASTER;
+      add_server_entry(d,work,myname,work->ServerType,0,
+                        lp_serverstring(),True);
 
-        DEBUG(3,("domain third stage: samba is now a domain master.\n"));
-        work->state = MST_DOMAIN; /* ... registering WORKGROUP(1b) succeeded */
+      DEBUG(0,("Samba is now a domain master browser for workgroup %s on subnet %s\n", 
+                work->work_group, inet_ntoa(d->bcast_ip)));
 
-        update_type |= SV_TYPE_DOMAIN_MASTER;
-      
-        if (lp_domain_logons())
-           {
-             update_type |= SV_TYPE_DOMAIN_CTRL;
-           }
+      break;
+    }
 
-               work->ServerType |= update_type;
-        add_server_entry(d,work,myname,work->ServerType,0,ServerComment,True);
+    case DOMAIN_MST:
+    {
+      /* don't have to do anything: just report success */
+      DEBUG(3,("domain second stage: there isn't one!\n"));
+      break;
+    }
+  }
+}
 
-               for (d1 = subnetlist; d1; d1 = d1->next)
-               {
-               struct work_record *w;
-                       if (ip_equal(d1->bcast_ip, d->bcast_ip)) continue;
 
-               for (w = d1->workgrouplist; w; w = w->next)
-                       {
-                               struct server_record *s = find_server(w, myname);
-                               if (strequal(w->work_group, work->work_group))
-                               {
-                                       w->ServerType |= update_type;
-                               }
-                               if (s)
-                               {
-                                       s->serv.type |= update_type;
-                                       DEBUG(4,("found server %s on %s: update to %8x\n",
-                                                                       s->serv.name, inet_ntoa(d1->bcast_ip),
-                                                                       s->serv.type));
-                               }
-                       }
-               }
-      }
+/*******************************************************************
+  become a logon server.
+  ******************************************************************/
+void become_logon_server(struct subnet_record *d, struct work_record *work)
+{
+  if (!work || !d) return;
   
+  if (lp_domain_logons())
+  {
+    DEBUG(0,("samba not configured as a logon master.\n"));
+    return;
+  }
+
+  DEBUG(2,("Becoming logon server for %s %s (currently at stage %d)\n",
+       work->work_group,inet_ntoa(d->bcast_ip),work->log_state));
+  
+  switch (work->log_state)
+  {
+    case LOGON_NONE: /* while we were nothing but a server... */
+    {
+      DEBUG(3,("go to first stage: register <1c> name\n"));
+            work->log_state = LOGON_WAIT;
+
+     /* XXXX the 0x1c is apparently something to do with domain logons */
+     add_my_name_entry(d, myworkgroup,0x1c,nb_type|NB_ACTIVE|NB_GROUP,False);
+
+      /* DON'T do anything else after calling add_my_name_entry() */
       break;
     }
 
-    case MST_DOMAIN:
+    case LOGON_WAIT:
     {
-      /* don't have to do anything: just report success */
-      DEBUG(3,("fifth stage: there isn't one yet!\n"));
+      work->log_state = LOGON_SRV; /* ... become logon server */
+      DEBUG(3,("logon second stage: register \n"));
+      /* update our server status */
+      work->ServerType |= SV_TYPE_NT|SV_TYPE_DOMAIN_MEMBER;
+      add_server_entry(d,work,myname,work->ServerType,0,
+                      lp_serverstring(),True);
+
+      /* DON'T do anything else after calling add_my_name_entry() */
+      break;
+    }
+
+    case LOGON_SRV:
+    {
+      DEBUG(3,("logon third stage: there isn't one!\n"));
       break;
     }
   }
@@ -419,53 +566,103 @@ void become_master(struct subnet_record *d, struct work_record *work)
 
 
 /*******************************************************************
-  unbecome the master browser. initates removal of necessary netbios 
+  unbecome the local master browser. initates removal of necessary netbios 
   names, and tells the world that we are no longer a master browser.
+
+  XXXX this _should_ be used to demote to a backup master browser, without
+  going straight to non-master browser.  another time.
+
   ******************************************************************/
-void become_nonmaster(struct subnet_record *d, struct work_record *work,
+void unbecome_local_master(struct subnet_record *d, struct work_record *work,
                                int remove_type)
 {
   int new_server_type = work->ServerType;
 
-  DEBUG(2,("Becoming non-master for %s\n",work->work_group));
-  
-  /* can only remove master or domain types with this function */
-  remove_type &= ~(SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER);
-
-  /* unbecome a master browser; unbecome a domain master, too :-( */
-  if (remove_type & SV_TYPE_MASTER_BROWSER)
-    remove_type |= SV_TYPE_DOMAIN_MASTER;
+  /* can only remove master types with this function */
+  remove_type &= SV_TYPE_MASTER_BROWSER;
 
   new_server_type &= ~remove_type;
 
-  if (!(new_server_type & (SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER)))
+  if (remove_type)
   {
+    DEBUG(2,("Becoming local non-master for %s\n",work->work_group));
+  
     /* no longer a master browser of any sort */
 
-       work->ServerType |= SV_TYPE_POTENTIAL_BROWSER;
+    work->ServerType |= SV_TYPE_POTENTIAL_BROWSER;
     work->ElectionCriterion &= ~0x4;
-    work->state = MST_NONE;
+    work->mst_state = MST_POTENTIAL;
 
        /* announce ourselves as no longer active as a master browser. */
     announce_server(d, work, work->work_group, myname, 0, 0);
-    remove_name_entry(d,MSBROWSE        ,0x01);
+    remove_name_entry(d,MSBROWSE        ,0x01,False);
+    remove_name_entry(d,work->work_group,0x1d,False);
   }
+}
+
+
+/*******************************************************************
+  unbecome the domain master browser. initates removal of necessary netbios 
+  names, and tells the world that we are no longer a domain browser.
+  ******************************************************************/
+void unbecome_domain_master(struct subnet_record *d, struct work_record *work,
+                               int remove_type)
+{
+  int new_server_type = work->ServerType;
+
+  DEBUG(2,("Becoming domain non-master for %s\n",work->work_group));
   
-  work->ServerType = new_server_type;
+  /* can only remove master or domain types with this function */
+  remove_type &= SV_TYPE_DOMAIN_MASTER;
+
+  new_server_type &= ~remove_type;
 
-  if (!(work->ServerType & SV_TYPE_DOMAIN_MASTER))
+  if (remove_type)
   {
-    if (work->state == MST_DOMAIN)
-      work->state = MST_BROWSER;
-    remove_name_entry(d,work->work_group,0x1b);
-    
+    /* no longer a domain master browser of any sort */
+
+    work->dom_state = DOMAIN_NONE;
+
+    /* announce ourselves as no longer active as a master browser on
+       all our local subnets. */
+    for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d))
+    {
+      work = find_workgroupstruct(d, myworkgroup, False);
+
+      announce_server(d, work, work->work_group, myname, 0, 0);
+      /* Remove the name entry without any NetBIOS traffic as that's
+         how it was registered. */
+      remove_name_entry(d,work->work_group,0x1b,True);    
+    }
   }
+}
 
-  if (!(work->ServerType & SV_TYPE_DOMAIN_MASTER))
+
+/*******************************************************************
+  unbecome the logon server. initates removal of necessary netbios 
+  names, and tells the world that we are no longer a logon server.
+  ******************************************************************/
+void unbecome_logon_server(struct subnet_record *d, struct work_record *work,
+                               int remove_type)
+{
+  int new_server_type = work->ServerType;
+
+  DEBUG(2,("Becoming logon non-server for %s\n",work->work_group));
+  
+  /* can only remove master or domain types with this function */
+  remove_type &= SV_TYPE_DOMAIN_MEMBER;
+
+  new_server_type &= ~remove_type;
+
+  if (remove_type)
   {
-    if (work->state >= MST_BROWSER)
-      work->state = MST_NONE;
-    remove_name_entry(d,work->work_group,0x1d);
+    /* no longer a master browser of any sort */
+
+    work->log_state = LOGON_NONE;
+
+       /* announce ourselves as no longer active as a master browser. */
+    announce_server(d, work, work->work_group, myname, 0, 0);
+    remove_name_entry(d,work->work_group,0x1c,False);    
   }
 }
 
@@ -473,9 +670,8 @@ void become_nonmaster(struct subnet_record *d, struct work_record *work,
 /*******************************************************************
   run the election
   ******************************************************************/
-void run_elections(void)
+void run_elections(time_t t)
 {
-  time_t t = time(NULL);
   static time_t lastime = 0;
   
   struct subnet_record *d;
@@ -485,9 +681,10 @@ void run_elections(void)
   
   lastime = t;
   
-  for (d = subnetlist; d; d = d->next)
+  for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d))
   {
     struct work_record *work;
+
     for (work = d->workgrouplist; work; work = work->next)
        {
          if (work->RunningElection)
@@ -502,9 +699,9 @@ void run_elections(void)
                           work->work_group,inet_ntoa(d->bcast_ip)));
                  
                  work->RunningElection = False;
-          work->state = MST_NONE;
+                 work->mst_state = MST_POTENTIAL;
 
-                 become_master(d, work);
+                 become_local_master(d, work);
                }
          }
        }
@@ -518,18 +715,31 @@ void run_elections(void)
 static BOOL win_election(struct work_record *work,int version,uint32 criterion,
                         int timeup,char *name)
 {  
-  time_t t = time(NULL);
-  uint32 mycriterion;
+  int mytimeup = time(NULL) - StartupTime;
+  uint32 mycriterion = work->ElectionCriterion;
+
+  /* If local master is false then never win
+     in election broadcasts. */
+  if(!lp_local_master())
+  {
+    DEBUG(3,("win_election: Losing election as local master == False\n"));
+    return False;
+  }
+  DEBUG(4,("election comparison: %x:%x %x:%x %d:%d %s:%s\n",
+          version,ELECTION_VERSION,
+          criterion,mycriterion,
+          timeup,mytimeup,
+          name,myname));
+
   if (version > ELECTION_VERSION) return(False);
   if (version < ELECTION_VERSION) return(True);
   
-  mycriterion = work->ElectionCriterion;
-
   if (criterion > mycriterion) return(False);
   if (criterion < mycriterion) return(True);
 
-  if (timeup > (t - StartupTime)) return(False);
-  if (timeup < (t - StartupTime)) return(True);
+  if (timeup > mytimeup) return(False);
+  if (timeup < mytimeup) return(True);
 
   if (strcasecmp(myname,name) > 0) return(False);
   
@@ -544,58 +754,61 @@ static BOOL win_election(struct work_record *work,int version,uint32 criterion,
   ******************************************************************/
 void process_election(struct packet_struct *p,char *buf)
 {
-  struct dgram_packet *dgram = &p->packet.dgram;
-  struct in_addr ip = dgram->header.source_ip;
-  struct subnet_record *d = find_subnet(ip);
-  int version = CVAL(buf,0);
-  uint32 criterion = IVAL(buf,1);
-  int timeup = IVAL(buf,5)/1000;
-  char *name = buf+13;
-  struct work_record *work;
+       struct dgram_packet *dgram = &p->packet.dgram;
+       struct in_addr ip = dgram->header.source_ip;
+       struct subnet_record *d = find_subnet(ip);
+       int version = CVAL(buf,0);
+       uint32 criterion = IVAL(buf,1);
+       int timeup = IVAL(buf,5)/1000;
+       char *name = buf+13;
+       struct work_record *work;
+
+       if (!d) return;
+
+       if (ip_equal(d->bcast_ip,wins_ip))
+       {
+               DEBUG(3,("Unexpected election request from %s %s on WINS net\n",
+                         name, inet_ntoa(p->ip)));
+               return;
+       }
 
-  if (!d) return;
-  
-  name[15] = 0;  
+       name[15] = 0;  
 
-  DEBUG(3,("Election request from %s vers=%d criterion=%08x timeup=%d\n",
-          name,version,criterion,timeup));
-  
-  if (same_context(dgram)) return;
-  
-  for (work = d->workgrouplist; work; work = work->next)
-    {
-      if (listening_name(work, &dgram->dest_name) && 
-         strequal(work->work_group, lp_workgroup()))
+       DEBUG(3,("Election request from %s %s vers=%d criterion=%08x timeup=%d\n",
+                 name,inet_ntoa(p->ip),version,criterion,timeup));
+
+       if (same_context(dgram)) return;
+
+       for (work = d->workgrouplist; work; work = work->next)
        {
-         if (win_election(work, version,criterion,timeup,name))
-           {
-             if (!work->RunningElection)
+               if (!strequal(work->work_group, myworkgroup))
+               continue;
+
+               if (win_election(work, version,criterion,timeup,name))
                {
-                 work->needelection = True;
-                 work->ElectionCount=0;
-          work->state = MST_NONE;
+                       if (!work->RunningElection)
+                       {
+                               work->needelection = True;
+                               work->ElectionCount=0;
+                               work->mst_state = MST_POTENTIAL;
+                       }
                }
-           }
-         else
-           {
-             work->needelection = False;
-             
-             if (work->RunningElection)
+               else
                {
-                 work->RunningElection = False;
-                 DEBUG(3,(">>> Lost election on %s %s <<<\n",
-                          work->work_group,inet_ntoa(d->bcast_ip)));
-                 
-                 /* if we are the master then remove our masterly names */
-                 if (AM_MASTER(work))
-                 {
-                     become_nonmaster(d, work,
-                                       SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER);
-                 }
+                       work->needelection = False;
+
+                       if (work->RunningElection || AM_MASTER(work))
+                       {
+                               work->RunningElection = False;
+                               DEBUG(3,(">>> Lost election on %s %s <<<\n",
+                                                 work->work_group,inet_ntoa(d->bcast_ip)));
+                               if (AM_MASTER(work))
+                               {
+                                       unbecome_local_master(d, work, SV_TYPE_MASTER_BROWSER);
+                               }
+                       }
                }
-           }
        }
-    }
 }
 
 
@@ -612,7 +825,7 @@ BOOL check_elections(void)
   struct subnet_record *d;
   BOOL run_any_election = False;
 
-  for (d = subnetlist; d; d = d->next)
+  for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d))
     {
       struct work_record *work;
       for (work = d->workgrouplist; work; work = work->next)