diff options
Diffstat (limited to 'source/nameserv.c')
-rw-r--r-- | source/nameserv.c | 2318 |
1 files changed, 2318 insertions, 0 deletions
diff --git a/source/nameserv.c b/source/nameserv.c new file mode 100644 index 00000000000..802b98ec0a0 --- /dev/null +++ b/source/nameserv.c @@ -0,0 +1,2318 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1995 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" +#include "loadparm.h" +#include "nameserv.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); + +extern int DEBUGLEVEL; + +extern pstring debugf; +pstring servicesf = CONFIGFILE; + +extern pstring scope; + +extern BOOL CanRecurse; + +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" + +#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 +****************************************************************************/ +static BOOL name_equal(struct nmb_name *n1,struct nmb_name *n2) +{ + if (n1->name_type != n2->name_type) return(False); + + return(strequal(n1->name,n2->name) && strequal(n1->scope,n2->scope)); +} + +/**************************************************************************** + add a netbios name into the namelist + **************************************************************************/ +static void add_name(struct name_record *n) +{ + struct name_record *n2; + + if (!namelist) { + namelist = n; + n->prev = NULL; + n->next = NULL; + return; + } + + for (n2 = namelist; n2->next; n2 = n2->next) ; + + n2->next = n; + n->next = NULL; + 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) +{ + struct name_record *nlist = namelist; + while (nlist && nlist != n) nlist = nlist->next; + if (nlist) { + if (nlist->next) nlist->next->prev = nlist->prev; + if (nlist->prev) nlist->prev->next = nlist->next; + free(nlist); + } +} + +/**************************************************************************** + find a name in the namelist + **************************************************************************/ +static struct name_record *find_name(struct nmb_name *n) +{ + struct name_record *ret; + for (ret = namelist; ret; ret = ret->next) + if (name_equal(&ret->name,n)) return(ret); + + return(NULL); +} + +/**************************************************************************** + dump a copy of the name table + **************************************************************************/ +static void dump_names(void) +{ + time_t t = time(NULL); + struct name_record *n; + struct domain_record *d; + + 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))); + } + + 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))); +} + + +/**************************************************************************** + add a host 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 *n; + struct name_record *n2=NULL; + + n = (struct name_record *)malloc(sizeof(*n)); + if (!n) return(NULL); + + bzero((char *)n,sizeof(*n)); + + make_nmb_name(&n->name,name,type,scope); + if ((n2=find_name(&n->name))) { + free(n); + n = n2; + } + + if (ttl) n->death_time = time(NULL)+ttl*3; + n->ip = ip; + n->unique = unique; + 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))); + + return(n); +} + + +/**************************************************************************** + add a domain entry + ****************************************************************************/ +static struct domain_record *add_domain_entry(char *name,struct in_addr ip) +{ + 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); +} + +/**************************************************************************** + add a server entry + ****************************************************************************/ +struct server_record *add_server_entry(char *name,int servertype, + int ttl,char *comment,BOOL replace) +{ + 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)); + + 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); +} + + +/**************************************************************************** + add the magic samba names, useful for finding samba servers + **************************************************************************/ +static 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; + + updatecount++; + + strcpy(fname,lp_lockdir()); + trim_string(fname,NULL,"/"); + strcat(fname,"/"); + strcat(fname,SERVER_LIST); + strcpy(fnamenew,fname); + strcat(fnamenew,"."); + + f = fopen(fnamenew,"w"); + + 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); + } + + + 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 + ******************************************************************/ +static void expire_names(void) +{ + 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; + + 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; + } + } + + /* 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; + } + } +} + + +/******************************************************************* + delete old names from the namelist + ******************************************************************/ +static void housekeeping(void) +{ + 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; + } + } +} + + +/**************************************************************************** + reload the services file + **************************************************************************/ +BOOL reload_services(BOOL test) +{ + BOOL ret; + extern fstring remote_machine; + + strcpy(remote_machine,"nmbd"); + + if (lp_loaded()) + { + pstring fname; + strcpy(fname,lp_configfile()); + if (file_exist(fname,NULL) && !strcsequal(fname,servicesf)) + { + strcpy(servicesf,fname); + test = False; + } + } + + 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); +} + + + +/**************************************************************************** +load a netbios hosts file +****************************************************************************/ +static void load_hosts_file(char *fname) +{ + FILE *f = fopen(fname,"r"); + pstring line; + if (!f) { + DEBUG(2,("Can't open lmhosts file %s\n",fname)); + return; + } + + while (!feof(f)) + { + if (!fgets_slash(line,sizeof(pstring),f)) continue; + + 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); + } + } + } + + fclose(f); +} + +/******************************************************************* + 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) +{ + 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); +} + + +/**************************************************************************** + send a backup list response + **************************************************************************/ +static void send_backup_list(char *name,int token,struct nmb_name *to, + struct in_addr ip) +{ + 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; + + 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); + + 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); + + 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); + + 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,("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) + { + 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; + } +} + + +/**************************************************************************** + 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: + { + 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)); + } + break; + case 7: + { + 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))); + } + 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"); + } + + { + 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); + } + } + + 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); +} + + +/**************************************************************************** +reply to a name status query +****************************************************************************/ +static void reply_name_status(struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + char *qname = nmb->question.question_name.name; + 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)); + + + 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 */ + + + buf = &nmb2->answers->rdata[0]; + SCVAL(buf,0,count); + buf += 1; + + for (n = namelist ; n; n = n->next) + { + if (n->source != SELF) continue; + + bzero(buf,18); + strcpy(buf,n->name.name); + strupper(buf); + buf[15] = n->name.name_type; + buf += 16; + buf[0] = 0x4; /* active */ + if (!n->unique) buf[0] |= 0x80; /* group */ + buf += 2; + count--; + } + + /* XXXXXXX we should fill in more fields of the statistics structure */ + bzero(buf,64); + { + extern int num_good_sends,num_good_receives; + SIVAL(buf,20,num_good_sends); + SIVAL(buf,24,num_good_receives); + } + SIVAL(buf,46,0xFFB8E5); /* undocumented - used by NT */ + + buf += 64; + + 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; + 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; + } + + /* 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; + } + + /* 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); + + /* 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; + } + + /* is our entry already dead? */ + if (n->death_time) { + if (n->death_time < p->timestamp) return; + ttl = n->death_time - p->timestamp; + } + + retip = n->ip; + unique = n->unique; + + /* it may have been an earlier failure */ + if (n->source == DNSFAIL) { + DEBUG(3,("DNSFAIL\n")); + return; + } + } + + /* 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); +} + + + +/* 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; + + +/******************************************************************* + 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) ; + + p->next = packet; + packet->next = NULL; + packet->prev = p; +} + +/**************************************************************************** + process a nmb packet + ****************************************************************************/ +static void process_nmb(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) + { + 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) + { + switch (nmb->question.question_type) + { + case 0x20: + reply_name_query(p); + break; + + case 0x21: + reply_name_status(p); + break; + } + return; + } + break; + + case 6: + if (nmb->header.qdcount>0 && + nmb->header.arcount>0) { + reply_name_release(p); + return; + } + break; + } + +} + + + +/******************************************************************* + 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) + { + 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(); + } +} + + +/**************************************************************************** + open the socket communication +****************************************************************************/ +static BOOL open_sockets(BOOL isdaemon,int port) +{ + struct hostent *hp; + + /* get host info */ + if ((hp = Get_Hostbyname(myhostname)) == 0) + { + DEBUG(0,( "Get_Hostbyname: Unknown host. %s\n",myhostname)); + return False; + } + + if (isdaemon) + ClientNMB = open_socket_in(SOCK_DGRAM, port,0); + 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; +} + + +/******************************************************************* + 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 +****************************************************************************/ +static BOOL init_structs(void ) +{ + 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; + + 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 (! *myname) { + char *p; + strcpy(myname,myhostname); + p = strchr(myname,'.'); + if (p) *p = 0; + } + + { + extern fstring local_machine; + strcpy(local_machine,myname); + strupper(local_machine); + } + + 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); + + charset_initialise(); + +#ifdef LMHOSTSFILE + strcpy(host_file,LMHOSTSFILE); +#endif + + /* this is for people who can't start the program correctly */ + while (argc > 1 && (*argv[1] != '-')) + { + argv++; + argc--; + } + + 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) + { + 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; + break; + case 'd': + DEBUGLEVEL = atoi(optarg); + break; + case 'p': + port = atoi(optarg); + break; + case 'h': + usage(argv[0]); + exit(0); + break; + default: + if (!is_a_socket(0)) + usage(argv[0]); + 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(); + } + + + DEBUG(3,("Opening sockets\n")); + + if (open_sockets(is_daemon,port)) + { + process(); + close_sockets(); + } + + if (dbf) + fclose(dbf); + return(0); +} |