LCOV - code coverage report
Current view: top level - source3/nmbd - nmbd_serverlistdb.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 118 163 72.4 %
Date: 2024-04-21 15:09:00 Functions: 10 11 90.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-1998
       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 "system/filesys.h"
      25             : #include "../librpc/gen_ndr/svcctl.h"
      26             : #include "nmbd/nmbd.h"
      27             : #include "lib/util/string_wrappers.h"
      28             : 
      29             : int updatecount = 0;
      30             : 
      31             : /*******************************************************************
      32             :   Remove all the servers in a work group.
      33             :   ******************************************************************/
      34             : 
      35           0 : void remove_all_servers(struct work_record *work)
      36             : {
      37             :         struct server_record *servrec;
      38             :         struct server_record *nexts;
      39             : 
      40           0 :         for (servrec = work->serverlist; servrec; servrec = nexts) {
      41           0 :                 DEBUG(7,("remove_all_servers: Removing server %s\n",servrec->serv.name));
      42           0 :                 nexts = servrec->next;
      43           0 :                 DLIST_REMOVE(work->serverlist, servrec);
      44           0 :                 ZERO_STRUCTP(servrec);
      45           0 :                 SAFE_FREE(servrec);
      46             :         }
      47             : 
      48           0 :         work->subnet->work_changed = True;
      49           0 : }
      50             : 
      51             : /***************************************************************************
      52             :   Add a server into a workgroup serverlist.
      53             :   **************************************************************************/
      54             : 
      55          77 : static void add_server_to_workgroup(struct work_record *work,
      56             :                              struct server_record *servrec)
      57             : {
      58          77 :         DLIST_ADD_END(work->serverlist, servrec);
      59          77 :         work->subnet->work_changed = True;
      60          77 : }
      61             : 
      62             : /****************************************************************************
      63             :   Find a server in a server list.
      64             :   **************************************************************************/
      65             : 
      66         748 : struct server_record *find_server_in_workgroup(struct work_record *work, const char *name)
      67             : {
      68             :         struct server_record *ret;
      69             :   
      70        1079 :         for (ret = work->serverlist; ret; ret = ret->next) {
      71         970 :                 if (strequal(ret->serv.name,name))
      72         639 :                         return ret;
      73             :         }
      74         109 :         return NULL;
      75             : }
      76             : 
      77             : 
      78             : /****************************************************************************
      79             :   Remove a server entry from this workgroup.
      80             :   ****************************************************************************/
      81             : 
      82          14 : void remove_server_from_workgroup(struct work_record *work, struct server_record *servrec)
      83             : {
      84          14 :         DLIST_REMOVE(work->serverlist, servrec);
      85          14 :         ZERO_STRUCTP(servrec);
      86          14 :         SAFE_FREE(servrec);
      87          14 :         work->subnet->work_changed = True;
      88          14 : }
      89             : 
      90             : /****************************************************************************
      91             :   Create a server entry on this workgroup.
      92             :   ****************************************************************************/
      93             : 
      94          77 : struct server_record *create_server_on_workgroup(struct work_record *work,
      95             :                                                  const char *name,int servertype, 
      96             :                                                  int ttl, const char *comment)
      97             : {
      98             :         struct server_record *servrec;
      99             :   
     100          77 :         if (name[0] == '*') {
     101           0 :                 DEBUG(7,("create_server_on_workgroup: not adding name starting with '*' (%s)\n",
     102             :                         name));
     103           0 :                 return (NULL);
     104             :         }
     105             :   
     106          77 :         if(find_server_in_workgroup(work, name) != NULL) {
     107           0 :                 DEBUG(0,("create_server_on_workgroup: Server %s already exists on \
     108             : workgroup %s. This is a bug.\n", name, work->work_group));
     109           0 :                 return NULL;
     110             :         }
     111             :   
     112          77 :         if((servrec = SMB_MALLOC_P(struct server_record)) == NULL) {
     113           0 :                 DEBUG(0,("create_server_entry_on_workgroup: malloc fail !\n"));
     114           0 :                 return NULL;
     115             :         }
     116             : 
     117          77 :         memset((char *)servrec,'\0',sizeof(*servrec));
     118             :  
     119          77 :         servrec->subnet = work->subnet;
     120             :  
     121          77 :         fstrcpy(servrec->serv.name,name);
     122          77 :         fstrcpy(servrec->serv.comment,comment);
     123          77 :         if (!strupper_m(servrec->serv.name)) {
     124           0 :                 DEBUG(2,("strupper_m %s failed\n", servrec->serv.name));
     125           0 :                 SAFE_FREE(servrec);
     126           0 :                 return NULL;
     127             :         }
     128          77 :         servrec->serv.type  = servertype;
     129             : 
     130          77 :         update_server_ttl(servrec, ttl);
     131             :   
     132          77 :         add_server_to_workgroup(work, servrec);
     133             :       
     134          77 :         DEBUG(3,("create_server_on_workgroup: Created server entry %s of type %x (%s) on \
     135             : workgroup %s.\n", name,servertype,comment, work->work_group));
     136             :  
     137          77 :         return(servrec);
     138             : }
     139             : 
     140             : /*******************************************************************
     141             :  Update the ttl field of a server record.
     142             : *******************************************************************/
     143             : 
     144         138 : void update_server_ttl(struct server_record *servrec, int ttl)
     145             : {
     146         138 :         if(ttl > lp_max_ttl())
     147           0 :                 ttl = lp_max_ttl();
     148             : 
     149         138 :         if(is_myname(servrec->serv.name))
     150          47 :                 servrec->death_time = PERMANENT_TTL;
     151             :         else
     152          91 :                 servrec->death_time = (ttl != PERMANENT_TTL) ? time(NULL)+(ttl*3) : PERMANENT_TTL;
     153         138 : }
     154             : 
     155             : /*******************************************************************
     156             :   Expire old servers in the serverlist. A time of -1 indicates 
     157             :   everybody dies except those with a death_time of PERMANENT_TTL (which is 0).
     158             :   This should only be called from expire_workgroups_and_servers().
     159             :   ******************************************************************/
     160             : 
     161        8470 : void expire_servers(struct work_record *work, time_t t)
     162             : {
     163             :         struct server_record *servrec;
     164             :         struct server_record *nexts;
     165             :   
     166       14009 :         for (servrec = work->serverlist; servrec; servrec = nexts) {
     167        5539 :                 nexts = servrec->next;
     168             : 
     169        5539 :                 if ((servrec->death_time != PERMANENT_TTL) && ((t == -1) || (servrec->death_time < t))) {
     170           2 :                         DEBUG(3,("expire_old_servers: Removing timed out server %s\n",servrec->serv.name));
     171           2 :                         remove_server_from_workgroup(work, servrec);
     172             :                 }
     173             :         }
     174        8470 : }
     175             : 
     176             : /*******************************************************************
     177             :  Decide if we should write out a server record for this server.
     178             :  We return zero if we should not. Check if we've already written
     179             :  out this server record from an earlier subnet.
     180             : ******************************************************************/
     181             : 
     182         225 : static uint32_t write_this_server_name( struct subnet_record *subrec,
     183             :                                       struct work_record *work,
     184             :                                       struct server_record *servrec)
     185             : {
     186             :         struct subnet_record *ssub;
     187             :         struct work_record *iwork;
     188             : 
     189             :         /* Go through all the subnets we have already seen. */
     190         225 :         for (ssub = FIRST_SUBNET; ssub && (ssub != subrec); ssub = NEXT_SUBNET_INCLUDING_UNICAST(ssub)) {
     191           0 :                 for(iwork = ssub->workgrouplist; iwork; iwork = iwork->next) {
     192           0 :                         if(find_server_in_workgroup( iwork, servrec->serv.name) != NULL) {
     193             :                                 /*
     194             :                                  * We have already written out this server record, don't
     195             :                                  * do it again. This gives precedence to servers we have seen
     196             :                                  * on the broadcast subnets over servers that may have been
     197             :                                  * added via a sync on the unicast_subet.
     198             :                                  *
     199             :                                  * The correct way to do this is to have a serverlist file
     200             :                                  * per subnet - this means changes to smbd as well. I may
     201             :                                  * add this at a later date (JRA).
     202             :                                  */
     203             : 
     204           0 :                                 return 0;
     205             :                         }
     206             :                 }
     207             :         }
     208             : 
     209         225 :         return servrec->serv.type;
     210             : }
     211             : 
     212             : /*******************************************************************
     213             :  Decide if we should write out a workgroup record for this workgroup.
     214             :  We return zero if we should not. Don't write out lp_workgroup() (we've
     215             :  already done it) and also don't write out a second workgroup record
     216             :  on the unicast subnet that we've already written out on one of the
     217             :  broadcast subnets.
     218             : ******************************************************************/
     219             : 
     220        1146 : static uint32_t write_this_workgroup_name( struct subnet_record *subrec,
     221             :                                          struct work_record *work)
     222             : {
     223             :         struct subnet_record *ssub;
     224             : 
     225        1146 :         if(strequal(lp_workgroup(), work->work_group))
     226         418 :                 return 0;
     227             : 
     228             :         /* This is a workgroup we have seen on a broadcast subnet. All
     229             :                 these have the same type. */
     230             : 
     231         728 :         if(subrec != unicast_subnet)
     232         728 :                 return (SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT|SV_TYPE_LOCAL_LIST_ONLY);
     233             : 
     234           0 :         for(ssub = FIRST_SUBNET; ssub;  ssub = NEXT_SUBNET_EXCLUDING_UNICAST(ssub)) {
     235             :                 /* This is the unicast subnet so check if we've already written out
     236             :                         this subnet when we passed over the broadcast subnets. */
     237             : 
     238           0 :                 if(find_workgroup_on_subnet( ssub, work->work_group) != NULL)
     239           0 :                         return 0;
     240             :         }
     241             : 
     242             :         /* All workgroups on the unicast subnet (except our own, which we
     243             :                 have already written out) cannot be local. */
     244             : 
     245           0 :         return (SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT);
     246             : }
     247             : 
     248             : /*******************************************************************
     249             :   Write out the browse.dat file.
     250             :   ******************************************************************/
     251             : 
     252        1847 : void write_browse_list_entry(FILE *fp, const char *name, uint32_t rec_type,
     253             :                 const char *local_master_browser_name, const char *description)
     254             : {
     255             :         fstring tmp;
     256             : 
     257        1847 :         slprintf(tmp,sizeof(tmp)-1, "\"%s\"", name);
     258        1847 :         fprintf(fp, "%-25s ", tmp);
     259        1847 :         fprintf(fp, "%08x ", rec_type);
     260        1847 :         slprintf(tmp, sizeof(tmp)-1, "\"%s\" ", local_master_browser_name);
     261        1847 :         fprintf(fp, "%-30s", tmp);
     262        1847 :         fprintf(fp, "\"%s\"\n", description);
     263        1847 : }
     264             : 
     265       17228 : void write_browse_list(time_t t, bool force_write)
     266             : {
     267             :         struct subnet_record *subrec;
     268             :         struct work_record *work;
     269             :         struct server_record *servrec;
     270             :         char *fname;
     271             :         char *fnamenew;
     272             :         uint32_t stype;
     273             :         int i;
     274             :         int fd;
     275             :         FILE *fp;
     276       17228 :         bool list_changed = force_write;
     277             :         static time_t lasttime = 0;
     278       17228 :         TALLOC_CTX *ctx = talloc_tos();
     279             :         const struct loadparm_substitution *lp_sub =
     280       17228 :                 loadparm_s3_global_substitution();
     281             : 
     282             :         /* Always dump if we're being told to by a signal. */
     283       17228 :         if(force_write == False) {
     284       17145 :                 if (!lasttime)
     285         126 :                         lasttime = t;
     286       17145 :                 if (t - lasttime < 5)
     287       13661 :                         return;
     288             :         }
     289             : 
     290        3567 :         lasttime = t;
     291             : 
     292        3567 :         dump_workgroups(force_write);
     293             : 
     294        6798 :         for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) {
     295        3567 :                 if(subrec->work_changed) {
     296         336 :                         list_changed = True;
     297         336 :                         break;
     298             :                 }
     299             :         }
     300             : 
     301        3567 :         if(!list_changed)
     302        3149 :                 return;
     303             : 
     304         418 :         updatecount++;
     305             : 
     306         418 :         fname = cache_path(talloc_tos(), SERVER_LIST);
     307         418 :         if (!fname) {
     308           0 :                 return;
     309             :         }
     310         418 :         fnamenew = talloc_asprintf(ctx, "%s.",
     311             :                                 fname);
     312         418 :         if (!fnamenew) {
     313           0 :                 talloc_free(fname);
     314           0 :                 return;
     315             :         }
     316             : 
     317         418 :         fd = open(fnamenew, O_WRONLY|O_CREAT|O_TRUNC, 0644);
     318         418 :         if (fd == -1) {
     319           0 :                 DBG_ERR("Can't open file %s: %s\n", fnamenew,
     320             :                         strerror(errno));
     321           0 :                 talloc_free(fnamenew);
     322           0 :                 talloc_free(fname);
     323           0 :                 return;
     324             :         }
     325             : 
     326         418 :         fp = fdopen(fd, "w");
     327         418 :         if (!fp) {
     328           0 :                 DBG_ERR("fdopen failed: %s\n", strerror(errno));
     329           0 :                 close(fd);
     330           0 :                 talloc_free(fnamenew);
     331           0 :                 talloc_free(fname);
     332           0 :                 return;
     333             :         }
     334         418 :         fd = -1;
     335             : 
     336             :         /*
     337             :          * Write out a record for our workgroup. Use the record from the first
     338             :          * subnet.
     339             :          */
     340             : 
     341         418 :         if((work = find_workgroup_on_subnet(FIRST_SUBNET, lp_workgroup())) == NULL) { 
     342           0 :                 DEBUG(0,("write_browse_list: Fatal error - cannot find my workgroup %s\n",
     343             :                         lp_workgroup()));
     344           0 :                 fclose(fp);
     345           0 :                 talloc_free(fnamenew);
     346           0 :                 talloc_free(fname);
     347           0 :                 return;
     348             :         }
     349             : 
     350         418 :         write_browse_list_entry(fp, work->work_group,
     351             :                 SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT|SV_TYPE_LOCAL_LIST_ONLY,
     352         418 :                 work->local_master_browser_name, work->work_group);
     353             : 
     354             :         /*
     355             :          * We need to do something special for our own names.
     356             :          * This is due to the fact that we may be a local master browser on
     357             :          * one of our broadcast subnets, and a domain master on the unicast
     358             :          * subnet. We iterate over the subnets and only write out the name
     359             :          * once.
     360             :          */
     361             : 
     362         894 :         for (i=0; my_netbios_names(i); i++) {
     363         476 :                 stype = 0;
     364         952 :                 for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) {
     365         476 :                         if((work = find_workgroup_on_subnet( subrec, lp_workgroup() )) == NULL)
     366           0 :                                 continue;
     367         476 :                         if((servrec = find_server_in_workgroup( work, my_netbios_names(i))) == NULL)
     368           0 :                                 continue;
     369             : 
     370         476 :                         stype |= servrec->serv.type;
     371             :                 }
     372             : 
     373             :                 /* Output server details, plus what workgroup they're in. */
     374         952 :                 write_browse_list_entry(fp, my_netbios_names(i), stype,
     375         476 :                         string_truncate(lp_server_string(talloc_tos(), lp_sub), MAX_SERVER_STRING_LENGTH), lp_workgroup());
     376             :         }
     377             : 
     378         836 :         for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) { 
     379         418 :                 subrec->work_changed = False;
     380             : 
     381        1564 :                 for (work = subrec->workgrouplist; work ; work = work->next) {
     382             :                         /* Write out a workgroup record for a workgroup. */
     383        1146 :                         uint32_t wg_type = write_this_workgroup_name( subrec, work);
     384             : 
     385        1146 :                         if(wg_type) {
     386         728 :                                 write_browse_list_entry(fp, work->work_group, wg_type,
     387         728 :                                                 work->local_master_browser_name,
     388         728 :                                                 work->work_group);
     389             :                         }
     390             : 
     391             :                         /* Now write out any server records a workgroup may have. */
     392             : 
     393        1847 :                         for (servrec = work->serverlist; servrec ; servrec = servrec->next) {
     394             :                                 uint32_t serv_type;
     395             : 
     396             :                                 /* We have already written our names here. */
     397         701 :                                 if(is_myname(servrec->serv.name))
     398         476 :                                         continue;
     399             : 
     400         225 :                                 serv_type = write_this_server_name(subrec, work, servrec);
     401         225 :                                 if(serv_type) {
     402             :                                         /* Output server details, plus what workgroup they're in. */
     403         225 :                                         write_browse_list_entry(fp, servrec->serv.name, serv_type,
     404         225 :                                                 servrec->serv.comment, work->work_group);
     405             :                                 }
     406             :                         }
     407             :                 }
     408             :         }
     409             : 
     410         418 :         fclose(fp);
     411         418 :         unlink(fname);
     412         418 :         chmod(fnamenew,0644);
     413         418 :         rename(fnamenew,fname);
     414         418 :         DEBUG(3,("write_browse_list: Wrote browse list into file %s\n",fname));
     415         418 :         talloc_free(fnamenew);
     416         418 :         talloc_free(fname);
     417             : }

Generated by: LCOV version 1.14