LCOV - code coverage report
Current view: top level - source3/nmbd - nmbd_elections.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 105 133 78.9 %
Date: 2024-04-21 15:09:00 Functions: 8 9 88.9 %

          Line data    Source code
       1             : /* 
       2             :    Unix SMB/CIFS implementation.
       3             :    NBT netbios routines and daemon - version 2
       4             :    Copyright (C) Andrew Tridgell 1994-1998
       5             :    Copyright (C) Luke Kenneth Casson Leighton 1994-1998
       6             :    Copyright (C) Jeremy Allison 1994-2003
       7             :    
       8             :    This program is free software; you can redistribute it and/or modify
       9             :    it under the terms of the GNU General Public License as published by
      10             :    the Free Software Foundation; either version 3 of the License, or
      11             :    (at your option) any later version.
      12             :    
      13             :    This program is distributed in the hope that it will be useful,
      14             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16             :    GNU General Public License for more details.
      17             :    
      18             :    You should have received a copy of the GNU General Public License
      19             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      20             :    
      21             : */
      22             : 
      23             : #include "includes.h"
      24             : #include "nmbd/nmbd.h"
      25             : #include "lib/util/string_wrappers.h"
      26             : 
      27             : /* Election parameters. */
      28             : extern time_t StartupTime;
      29             : 
      30             : /****************************************************************************
      31             :   Send an election datagram packet.
      32             : **************************************************************************/
      33             : 
      34         164 : static void send_election_dgram(struct subnet_record *subrec, const char *workgroup_name,
      35             :                                 uint32_t criterion, int timeup,const char *server_name)
      36             : {
      37             :         char outbuf[1024];
      38             :         unstring srv_name;
      39             :         char *p;
      40             : 
      41         164 :         DEBUG(2,("send_election_dgram: Sending election packet for workgroup %s on subnet %s\n",
      42             :                 workgroup_name, subrec->subnet_name ));
      43             : 
      44         164 :         memset(outbuf,'\0',sizeof(outbuf));
      45         164 :         p = outbuf;
      46         164 :         SCVAL(p,0,ANN_Election); /* Election opcode. */
      47         164 :         p++;
      48             : 
      49         164 :         SCVAL(p,0,((criterion == 0 && timeup == 0) ? 0 : ELECTION_VERSION));
      50         164 :         SIVAL(p,1,criterion);
      51         164 :         SIVAL(p,5,timeup*1000); /* ms - Despite what the spec says. */
      52         164 :         p += 13;
      53         164 :         unstrcpy(srv_name, server_name);
      54         164 :         if (!strupper_m(srv_name)) {
      55           0 :                 DEBUG(2,("strupper_m failed for %s\n", srv_name));
      56           0 :                 return;
      57             :         }
      58             :         /* The following call does UNIX -> DOS charset conversion. */
      59         164 :         push_ascii(p, srv_name, sizeof(outbuf)-PTR_DIFF(p,outbuf)-1, STR_TERMINATE);
      60         164 :         p = skip_string(outbuf,sizeof(outbuf),p);
      61             : 
      62         164 :         send_mailslot(False, BROWSE_MAILSLOT, outbuf, PTR_DIFF(p,outbuf),
      63             :                 lp_netbios_name(), 0,
      64             :                 workgroup_name, 0x1e,
      65             :                 subrec->bcast_ip, subrec->myip, DGRAM_PORT);
      66             : }
      67             : 
      68             : /*******************************************************************
      69             :  We found a current master browser on one of our broadcast interfaces.
      70             : ******************************************************************/
      71             : 
      72          34 : static void check_for_master_browser_success(struct subnet_record *subrec,
      73             :                                  struct userdata_struct *userdata,
      74             :                                  struct nmb_name *answer_name,
      75             :                                  struct in_addr answer_ip, struct res_rec *rrec)
      76             : {
      77             :         unstring aname;
      78          34 :         pull_ascii_nstring(aname, sizeof(aname), answer_name->name);
      79          34 :         DEBUG(3,("check_for_master_browser_success: Local master browser for workgroup %s exists at \
      80             : IP %s (just checking).\n", aname, inet_ntoa(answer_ip) ));
      81          34 : }
      82             : 
      83             : /*******************************************************************
      84             :  We failed to find a current master browser on one of our broadcast interfaces.
      85             : ******************************************************************/
      86             : 
      87          34 : static void check_for_master_browser_fail( struct subnet_record *subrec,
      88             :                                            struct response_record *rrec,
      89             :                                            struct nmb_name *question_name,
      90             :                                            int fail_code)
      91             : {
      92             :         unstring workgroup_name;
      93             :         struct work_record *work;
      94             : 
      95          34 :         pull_ascii_nstring(workgroup_name,sizeof(workgroup_name),question_name->name);
      96             : 
      97          34 :         work = find_workgroup_on_subnet(subrec, workgroup_name);
      98          34 :         if(work == NULL) {
      99           0 :                 DEBUG(0,("check_for_master_browser_fail: Unable to find workgroup %s on subnet %s.=\n",
     100             :                         workgroup_name, subrec->subnet_name ));
     101           0 :                 return;
     102             :         }
     103             : 
     104          34 :         if (strequal(work->work_group, lp_workgroup())) {
     105             : 
     106          34 :                 if (lp_local_master()) {
     107             :                         /* We have discovered that there is no local master
     108             :                                 browser, and we are configured to initiate
     109             :                                 an election that we will participate in.
     110             :                         */
     111          34 :                         DEBUG(2,("check_for_master_browser_fail: Forcing election on workgroup %s subnet %s\n",
     112             :                                 work->work_group, subrec->subnet_name ));
     113             : 
     114             :                         /* Setting this means we will participate when the
     115             :                                 election is run in run_elections(). */
     116          34 :                         work->needelection = True;
     117             :                 } else {
     118             :                         /* We need to force an election, because we are configured
     119             :                                 not to become the local master, but we still need one,
     120             :                                 having detected that one doesn't exist.
     121             :                         */
     122           0 :                         send_election_dgram(subrec, work->work_group, 0, 0, "");
     123             :                 }
     124             :         }
     125             : }
     126             : 
     127             : /*******************************************************************
     128             :   Ensure there is a local master browser for a workgroup on our
     129             :   broadcast interfaces.
     130             : ******************************************************************/
     131             : 
     132       17145 : void check_master_browser_exists(time_t t)
     133             : {
     134             :         static time_t lastrun=0;
     135             :         struct subnet_record *subrec;
     136       17145 :         const char *workgroup_name = lp_workgroup();
     137             : 
     138       17145 :         if (t < (lastrun + (CHECK_TIME_MST_BROWSE * 60)))
     139       17013 :                 return;
     140             : 
     141         132 :         lastrun = t;
     142             : 
     143         132 :         dump_workgroups(False);
     144             : 
     145         264 :         for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
     146             :                 struct work_record *work;
     147             : 
     148         414 :                 for (work = subrec->workgrouplist; work; work = work->next) {
     149         282 :                         if (strequal(work->work_group, workgroup_name) && !AM_LOCAL_MASTER_BROWSER(work)) {
     150             :                                 /* Do a name query for the local master browser on this net. */
     151          68 :                                 query_name( subrec, work->work_group, 0x1d,
     152             :                                         check_for_master_browser_success,
     153             :                                         check_for_master_browser_fail,
     154             :                                         NULL);
     155             :                         }
     156             :                 }
     157             :         }
     158             : }
     159             : 
     160             : /*******************************************************************
     161             :   Run an election.
     162             : ******************************************************************/
     163             : 
     164       17145 : void run_elections(time_t t)
     165             : {
     166             :         static time_t lastime = 0;
     167             :   
     168             :         struct subnet_record *subrec;
     169             :   
     170             :         /* Send election packets once every 2 seconds - note */
     171       17145 :         if (lastime && (t - lastime < 2)) {
     172       12848 :                 return;
     173             :         }
     174             :   
     175        4297 :         lastime = t;
     176             :   
     177        8594 :         for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
     178             :                 struct work_record *work;
     179             : 
     180       14030 :                 for (work = subrec->workgrouplist; work; work = work->next) {
     181        9733 :                         if (work->RunningElection) {
     182             :                                 /*
     183             :                                  * We can only run an election for a workgroup if we have
     184             :                                  * registered the WORKGROUP<1e> name, as that's the name
     185             :                                  * we must listen to.
     186             :                                  */
     187             :                                 struct nmb_name nmbname;
     188             : 
     189         164 :                                 make_nmb_name(&nmbname, work->work_group, 0x1e);
     190         164 :                                 if(find_name_on_subnet( subrec, &nmbname, FIND_SELF_NAME)==NULL) {
     191           0 :                                         DEBUG(8,("run_elections: Cannot send election packet yet as name %s not \
     192             : yet registered on subnet %s\n", nmb_namestr(&nmbname), subrec->subnet_name ));
     193           0 :                                         continue;
     194             :                                 }
     195             : 
     196         164 :                                 send_election_dgram(subrec, work->work_group, work->ElectionCriterion,
     197         164 :                                                 t - StartupTime, lp_netbios_name());
     198             :               
     199         164 :                                 if (work->ElectionCount++ >= 4) {
     200             :                                         /* Won election (4 packets were sent out uncontested. */
     201          32 :                                         DEBUG(2,("run_elections: >>> Won election for workgroup %s on subnet %s <<<\n",
     202             :                                                 work->work_group, subrec->subnet_name ));
     203             : 
     204          32 :                                         work->RunningElection = False;
     205             : 
     206          32 :                                         become_local_master_browser(subrec, work);
     207             :                                 }
     208             :                         }
     209             :                 }
     210             :         }
     211             : }
     212             : 
     213             : /*******************************************************************
     214             :   Determine if I win an election.
     215             : ******************************************************************/
     216             : 
     217          38 : static bool win_election(struct work_record *work, int version,
     218             :                          uint32_t criterion, int timeup, const char *server_name)
     219             : {  
     220          38 :         int mytimeup = time(NULL) - StartupTime;
     221          38 :         uint32_t mycriterion = work->ElectionCriterion;
     222             : 
     223             :         /* If local master is false then never win in election broadcasts. */
     224          38 :         if(!lp_local_master()) {
     225           0 :                 DEBUG(3,("win_election: Losing election as local master == False\n"));
     226           0 :                 return False;
     227             :         }
     228             :  
     229          38 :         DEBUG(4,("win_election: election comparison: %x:%x %x:%x %d:%d %s:%s\n",
     230             :                         version, ELECTION_VERSION,
     231             :                         criterion, mycriterion,
     232             :                         timeup, mytimeup,
     233             :                         server_name, lp_netbios_name()));
     234             : 
     235          38 :         if (version > ELECTION_VERSION)
     236           0 :                 return(False);
     237          38 :         if (version < ELECTION_VERSION)
     238           0 :                 return(True);
     239             :   
     240          38 :         if (criterion > mycriterion)
     241          32 :                 return(False);
     242           6 :         if (criterion < mycriterion)
     243           3 :                 return(True);
     244             : 
     245           3 :         if (timeup > mytimeup)
     246           3 :                 return(False);
     247           0 :         if (timeup < mytimeup)
     248           0 :                 return(True);
     249             : 
     250           0 :         if (strcasecmp_m(lp_netbios_name(), server_name) > 0)
     251           0 :                 return(False);
     252             :   
     253           0 :         return(True);
     254             : }
     255             : 
     256             : /*******************************************************************
     257             :   Process an incoming election datagram packet.
     258             : ******************************************************************/
     259             : 
     260          38 : void process_election(struct subnet_record *subrec, struct packet_struct *p, const char *buf)
     261             : {
     262          38 :         struct dgram_packet *dgram = &p->packet.dgram;
     263          38 :         int version = CVAL(buf,0);
     264          38 :         uint32_t criterion = IVAL(buf,1);
     265          38 :         int timeup = IVAL(buf,5)/1000;
     266             :         unstring server_name;
     267             :         struct work_record *work;
     268             :         unstring workgroup_name;
     269             : 
     270          38 :         pull_ascii_nstring(server_name, sizeof(server_name), buf+13);
     271          38 :         pull_ascii_nstring(workgroup_name, sizeof(workgroup_name), dgram->dest_name.name);
     272             : 
     273          38 :         server_name[15] = 0;  
     274             : 
     275          38 :         DEBUG(3,("process_election: Election request from %s at IP %s on subnet %s for workgroup %s.\n",
     276             :                 server_name,inet_ntoa(p->ip), subrec->subnet_name, workgroup_name ));
     277             : 
     278          38 :         DEBUG(5,("process_election: vers=%d criterion=%08x timeup=%d\n", version,criterion,timeup));
     279             : 
     280          38 :         if(( work = find_workgroup_on_subnet(subrec, workgroup_name)) == NULL) {
     281           0 :                 DEBUG(0,("process_election: Cannot find workgroup %s on subnet %s.\n",
     282             :                         workgroup_name, subrec->subnet_name ));
     283           0 :                 goto done;
     284             :         }
     285             : 
     286          38 :         if (!strequal(work->work_group, lp_workgroup())) {
     287           0 :                 DEBUG(3,("process_election: ignoring election request for workgroup %s on subnet %s as this \
     288             : is not my workgroup.\n", work->work_group, subrec->subnet_name ));
     289           0 :                 goto done;
     290             :         }
     291             : 
     292          38 :         if (win_election(work, version,criterion,timeup,server_name)) {
     293             :                 /* We take precedence over the requesting server. */
     294           3 :                 if (!work->RunningElection) {
     295             :                         /* We weren't running an election - start running one. */
     296             : 
     297           2 :                         work->needelection = True;
     298           2 :                         work->ElectionCount=0;
     299             :                 }
     300             : 
     301             :                 /* Note that if we were running an election for this workgroup on this
     302             :                         subnet already, we just ignore the server we take precedence over. */
     303             :         } else {
     304             :                 /* We lost. Stop participating. */
     305          35 :                 work->needelection = False;
     306             : 
     307          35 :                 if (work->RunningElection || AM_LOCAL_MASTER_BROWSER(work)) {
     308           6 :                         work->RunningElection = False;
     309           6 :                         DEBUG(3,("process_election: >>> Lost election for workgroup %s on subnet %s <<<\n",
     310             :                                 work->work_group, subrec->subnet_name ));
     311           6 :                         if (AM_LOCAL_MASTER_BROWSER(work))
     312           2 :                                 unbecome_local_master_browser(subrec, work, False);
     313             :                 }
     314             :         }
     315          33 : done:
     316          38 :         return;
     317             : }
     318             : 
     319             : /****************************************************************************
     320             :   This function looks over all the workgroups known on all the broadcast
     321             :   subnets and decides if a browser election is to be run on that workgroup.
     322             :   It returns True if any election packets need to be sent (this will then
     323             :   be done by run_elections().
     324             : ***************************************************************************/
     325             : 
     326       17188 : bool check_elections(void)
     327             : {
     328             :         struct subnet_record *subrec;
     329       17188 :         bool run_any_election = False;
     330             : 
     331       34376 :         for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
     332             :                 struct work_record *work;
     333       48076 :                 for (work = subrec->workgrouplist; work; work = work->next) {
     334       30888 :                         if (work->RunningElection) {
     335        2143 :                                 run_any_election = work->RunningElection;
     336             :                         }
     337             : 
     338             :                         /* 
     339             :                          * Start an election if we have any chance of winning.
     340             :                          * Note this is a change to the previous code, that would
     341             :                          * only run an election if nmbd was in the potential browser
     342             :                          * state. We need to run elections in any state if we're told
     343             :                          * to. JRA.
     344             :                          */
     345             : 
     346       30888 :                         if (work->needelection && !work->RunningElection && lp_local_master()) {
     347             :                                 /*
     348             :                                  * We can only run an election for a workgroup if we have
     349             :                                  * registered the WORKGROUP<1e> name, as that's the name
     350             :                                  * we must listen to.
     351             :                                  */
     352             :                                 struct nmb_name nmbname;
     353             : 
     354         623 :                                 make_nmb_name(&nmbname, work->work_group, 0x1e);
     355         623 :                                 if(find_name_on_subnet( subrec, &nmbname, FIND_SELF_NAME)==NULL) {
     356         586 :                                         DEBUG(8,("check_elections: Cannot send election packet yet as name %s not \
     357             : yet registered on subnet %s\n", nmb_namestr(&nmbname), subrec->subnet_name ));
     358         586 :                                         continue;
     359             :                                 }
     360             : 
     361          37 :                                 DEBUG(3,("check_elections: >>> Starting election for workgroup %s on subnet %s <<<\n",
     362             :                                         work->work_group, subrec->subnet_name ));
     363             : 
     364          37 :                                 work->ElectionCount = 0;
     365          37 :                                 work->RunningElection = True;
     366          37 :                                 work->needelection = False;
     367             :                         }
     368             :                 }
     369             :         }
     370       17188 :         return run_any_election;
     371             : }
     372             : 
     373             : /****************************************************************************
     374             :  Process a internal Samba message forcing an election.
     375             : ***************************************************************************/
     376             : 
     377           0 : void nmbd_message_election(struct messaging_context *msg,
     378             :                            void *private_data,
     379             :                            uint32_t msg_type,
     380             :                            struct server_id server_id,
     381             :                            DATA_BLOB *data)
     382             : {
     383             :         struct subnet_record *subrec;
     384             : 
     385           0 :         for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
     386             :                 struct work_record *work;
     387           0 :                 for (work = subrec->workgrouplist; work; work = work->next) {
     388           0 :                         if (strequal(work->work_group, lp_workgroup())) {
     389           0 :                                 work->needelection = True;
     390           0 :                                 work->ElectionCount=0;
     391           0 :                                 work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE;
     392             :                         }
     393             :                 }
     394             :         }
     395           0 : }

Generated by: LCOV version 1.14