From a2c1623827406667a4f2f058c24f1d971f6627f8 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Tue, 4 Jun 1996 06:42:03 +0000 Subject: a huge pile of changes :-) The biggest thing is the integration of Lukes new nmbd. Its still largely untested, so we will really need some feedback I've also added auto prototype generation and cleaned up a lot of minor things as a result (This used to be commit 0d8dcfa13c527ec2c8aca39ba49c09e4e694b26c) --- source3/nameserv.c | 2848 +++++++++++++++------------------------------------- 1 file changed, 791 insertions(+), 2057 deletions(-) (limited to 'source3/nameserv.c') diff --git a/source3/nameserv.c b/source3/nameserv.c index b64a934572..4cd9b099f0 100644 --- a/source3/nameserv.c +++ b/source3/nameserv.c @@ -18,202 +18,32 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + Revision History: + + 14 jan 96: lkcl@pires.co.uk + added multiple workgroup domain master support + */ #include "includes.h" #include "loadparm.h" -#include "nameserv.h" +#include "localnet.h" -static void queue_packet(struct packet_struct *packet); -void process(void); -static void dump_names(void); -static void announce_request(char *group); -void sync_browse_lists(char *name,int name_type,char *myname, - char *domain,struct in_addr ip); +enum name_search { FIND_SELF, FIND_GLOBAL }; extern int DEBUGLEVEL; -extern pstring debugf; -pstring servicesf = CONFIGFILE; - extern pstring scope; - extern BOOL CanRecurse; +extern pstring myname; +extern struct in_addr ipzero; -extern struct in_addr myip; -extern struct in_addr bcast_ip; -extern struct in_addr Netmask; -extern pstring myhostname; -static pstring host_file; -static pstring myname=""; - -static int ClientNMB= -1; -static int ClientDGRAM= -1; - -static BOOL needannounce=True; - -/* this is our name database */ -static struct name_record *namelist = NULL; - -/* list of servers to be returned by NetServerEnum */ -static struct server_record *serverlist = NULL; - -/* this is the domain list. For the moment we will assume that our - primary domain is the first one listed in this list */ -static struct domain_record *domainlist = NULL; - -/* are we running as a daemon ? */ -static BOOL is_daemon = False; - -/* machine comment for host announcements */ -static pstring ServerComment=""; - -static BOOL got_bcast = False; -static BOOL got_myip = False; -static BOOL got_nmask = False; - -static BOOL updatedlists = False; -static int updatecount=0; - -/* what server type are we currently */ -static int ServerType = -SV_TYPE_WORKSTATION | SV_TYPE_SERVER | SV_TYPE_TIME_SOURCE | -SV_TYPE_SERVER_UNIX | -SV_TYPE_PRINTQ_SERVER | SV_TYPE_POTENTIAL_BROWSER; - -/* here are my election parameters */ - -/* NTAS uses 2, NT uses 1, WfWg uses 0 */ -#define MAINTAIN_LIST 1 -#define ELECTION_VERSION 1 - -static BOOL RunningElection = False; -static BOOL needelection = False; -static int ElectionCount = 0; -static int StartupTime =0; - - -/* WfWg uses 01040b01 */ -/* Win95 uses 01041501 */ -/* NTAS uses ?? */ -static uint32 ElectionCriterion = (MAINTAIN_LIST<<1)|(ELECTION_VERSION<<8); - -/* we currently support being the master for just one group. Being the - master for more than one group might be tricky as NetServerEnum is - often asked for a list without naming the group */ -static fstring PrimaryGroup=""; - -#define AM_MASTER (PrimaryGroup[0] && (ServerType & SV_TYPE_MASTER_BROWSER)) - -#define MSBROWSE "\001\002__MSBROWSE__\002" +/* netbios names database */ +struct name_record *namelist; #define GET_TTL(ttl) ((ttl)?MIN(ttl,lp_max_ttl()):lp_max_ttl()) -#define BROWSE_MAILSLOT "\\MAILSLOT\\BROWSE" - -/**************************************************************************** -catch a sighup -****************************************************************************/ -static int sig_hup() -{ - BlockSignals(True); - - DEBUG(0,("Got SIGHUP (reload not implemented)\n")); - dump_names(); - reload_services(True); - - BlockSignals(False); -#ifndef DONT_REINSTALL_SIG - signal(SIGHUP,SIGNAL_CAST sig_hup); -#endif - return(0); -} - -/**************************************************************************** -catch a sigpipe -****************************************************************************/ -static int sig_pipe() -{ - BlockSignals(True); - - DEBUG(0,("Got SIGPIPE\n")); - if (!is_daemon) - exit(1); - BlockSignals(False); - return(0); -} - -#if DUMP_CORE -/******************************************************************* -prepare to dump a core file - carefully! -********************************************************************/ -static BOOL dump_core(void) -{ - char *p; - pstring dname; - strcpy(dname,debugf); - if ((p=strrchr(dname,'/'))) *p=0; - strcat(dname,"/corefiles"); - mkdir(dname,0700); - sys_chown(dname,getuid(),getgid()); - chmod(dname,0700); - if (chdir(dname)) return(False); - umask(~(0700)); - -#ifndef NO_GETRLIMIT -#ifdef RLIMIT_CORE - { - struct rlimit rlp; - getrlimit(RLIMIT_CORE, &rlp); - rlp.rlim_cur = MAX(4*1024*1024,rlp.rlim_cur); - setrlimit(RLIMIT_CORE, &rlp); - getrlimit(RLIMIT_CORE, &rlp); - DEBUG(3,("Core limits now %d %d\n",rlp.rlim_cur,rlp.rlim_max)); - } -#endif -#endif - - - DEBUG(0,("Dumping core in %s\n",dname)); - return(True); -} -#endif - - -/**************************************************************************** -possibly continue after a fault -****************************************************************************/ -static void fault_continue(void) -{ - static int errcount=1; - - errcount--; - - if (is_daemon && errcount) - process(); - -#if DUMP_CORE - if (dump_core()) return; -#endif - - return; -} - - -/******************************************************************* - wrapper to get the DC - ******************************************************************/ -static char *domain_controller(void) -{ - char *dc = lp_domain_controller(); - /* so many people mistake this for a bool that we need to handle it. sigh. */ - if (!*dc || strequal(dc,"yes") || strequal(dc,"true")) - strcpy(dc,myname); - return(dc); -} - - /**************************************************************************** true if two netbios names are equal @@ -232,7 +62,8 @@ static void add_name(struct name_record *n) { struct name_record *n2; - if (!namelist) { + if (!namelist) + { namelist = n; n->prev = NULL; n->next = NULL; @@ -246,58 +77,18 @@ static void add_name(struct name_record *n) n->prev = n2; } -/**************************************************************************** - add a domain into the list - **************************************************************************/ -static void add_domain(struct domain_record *d) -{ - struct domain_record *d2; - - if (!domainlist) { - domainlist = d; - d->prev = NULL; - d->next = NULL; - return; - } - - for (d2 = domainlist; d2->next; d2 = d2->next) ; - - d2->next = d; - d->next = NULL; - d->prev = d2; -} - - -/**************************************************************************** - add a server into the list - **************************************************************************/ -static void add_server(struct server_record *s) -{ - struct server_record *s2; - - if (!serverlist) { - serverlist = s; - s->prev = NULL; - s->next = NULL; - return; - } - - for (s2 = serverlist; s2->next; s2 = s2->next) ; - - s2->next = s; - s->next = NULL; - s->prev = s2; -} - /**************************************************************************** remove a name from the namelist. The pointer must be an element just retrieved **************************************************************************/ -static void remove_name(struct name_record *n) +void remove_name(struct name_record *n) { struct name_record *nlist = namelist; + while (nlist && nlist != n) nlist = nlist->next; - if (nlist) { + + if (nlist) + { if (nlist->next) nlist->next->prev = nlist->prev; if (nlist->prev) nlist->prev->next = nlist->next; free(nlist); @@ -305,48 +96,80 @@ static void remove_name(struct name_record *n) } /**************************************************************************** - find a name in the namelist + find a name in the domain database namelist + search can be: + FIND_SELF - look for names the samba server has added for itself + FIND_GLOBAL - the name can be anyone. first look on the client's + subnet, then the server's subnet, then all subnets. **************************************************************************/ -static struct name_record *find_name(struct nmb_name *n) +static struct name_record *find_name_search(struct nmb_name *name, enum name_search search, + struct in_addr ip) { - struct name_record *ret; - for (ret = namelist; ret; ret = ret->next) - if (name_equal(&ret->name,n)) return(ret); + struct name_record *ret; + + /* any number of winpopup names can be added. must search by ip as well */ + if (name->name_type != 0x3) ip = ipzero; + + for (ret = namelist; ret; ret = ret->next) + { + if (name_equal(&ret->name,name)) + { + /* self search: self names only */ + if (search == FIND_SELF && ret->source != SELF) continue; + + if (zero_ip(ip) || ip_equal(ip, ret->ip)) + { + return ret; + } + } + } - return(NULL); + return NULL; } + /**************************************************************************** dump a copy of the name table **************************************************************************/ -static void dump_names(void) +void dump_names(void) { - time_t t = time(NULL); - struct name_record *n; - struct domain_record *d; + struct name_record *n; + time_t t = time(NULL); + + DEBUG(3,("Dump of local name table:\n")); + + for (n = namelist; n; n = n->next) + { + DEBUG(3,("%s %s TTL=%d NBFLAGS=%2x\n", + namestr(&n->name), + inet_ntoa(n->ip), + n->death_time?n->death_time-t:0, + n->nb_flags)); + } +} + - DEBUG(3,("Dump of local name table:\n")); +/**************************************************************************** + remove an entry from the name list + ****************************************************************************/ +void remove_netbios_name(char *name,int type, enum name_source source, + struct in_addr ip) +{ + struct nmb_name nn; + struct name_record *n; - 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))); - } + make_nmb_name(&nn, name, type, scope); + n = find_name_search(&nn, FIND_GLOBAL, ip); - 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))); + if (n && n->source == source) remove_name(n); } /**************************************************************************** - add a host entry to the name list + add an entry to the name list ****************************************************************************/ -static struct name_record *add_host_entry(char *name,int type,BOOL unique,int ttl, - enum name_source source, - struct in_addr ip) +struct name_record *add_netbios_entry(char *name, int type, int nb_flags, int ttl, + enum name_source source, struct in_addr ip) { struct name_record *n; struct name_record *n2=NULL; @@ -357,1967 +180,878 @@ static struct name_record *add_host_entry(char *name,int type,BOOL unique,int tt bzero((char *)n,sizeof(*n)); make_nmb_name(&n->name,name,type,scope); - if ((n2=find_name(&n->name))) { + + if ((n2 = find_name_search(&n->name, FIND_GLOBAL, ip))) + { free(n); n = n2; } if (ttl) n->death_time = time(NULL)+ttl*3; n->ip = ip; - n->unique = unique; + n->nb_flags = nb_flags; n->source = source; if (!n2) add_name(n); - DEBUG(3,("Added host entry %s at %s ttl=%d unique=%s\n", - namestr(&n->name),inet_ntoa(ip),ttl,BOOLSTR(unique))); + DEBUG(3,("Added netbios name %s at %s ttl=%d nb_flags=%2x\n", + namestr(&n->name),inet_ntoa(ip),ttl,nb_flags)); return(n); } /**************************************************************************** - add a domain entry + remove an entry from the name list ****************************************************************************/ -static struct domain_record *add_domain_entry(char *name,struct in_addr ip) +void remove_name_entry(char *name,int type) { - struct domain_record *d; - - d = (struct domain_record *)malloc(sizeof(*d)); - - if (!d) return(NULL); - - bzero((char *)d,sizeof(*d)); - - if (zero_ip(ip)) ip = bcast_ip; - - StrnCpy(d->name,name,sizeof(d->name)-1); - d->bcast_ip = ip; - - if (!PrimaryGroup[0] && ip_equal(bcast_ip,ip) && name[0] != '*') { - strcpy(PrimaryGroup,name); - strupper(PrimaryGroup); - DEBUG(3,("Setting primary group to %s (%s)\n",PrimaryGroup,inet_ntoa(ip))); - } - - add_domain(d); - - ip = *interpret_addr2("255.255.255.255"); - if (name[0] != '*') add_host_entry(name,0x1e,False,0,SELF,ip); - - DEBUG(3,("Added domain entry %s at %s\n", - name,inet_ntoa(ip))); + if (lp_wins_support()) + { + /* we are a WINS server. */ + remove_netbios_name(name,type,SELF,myip); + } + else + { + struct in_addr ip; + ip = ipzero; - return(d); + queue_netbios_pkt_wins(ClientNMB,NMB_REL,NAME_RELEASE, + name, type, 0, + False, True, ip); + } } + /**************************************************************************** - add a server entry + add an entry to the name list ****************************************************************************/ -struct server_record *add_server_entry(char *name,int servertype, - int ttl,char *comment,BOOL replace) +void add_name_entry(char *name,int type,int nb_flags) { - BOOL newentry=False; - struct server_record *s; - - for (s = serverlist; s; s = s->next) - if (strequal(name,s->name)) break; - - if (s && !replace) { - DEBUG(4,("Not replacing %s\n",name)); - return(s); - } - - updatedlists=True; - - if (!s) { - newentry = True; - s = (struct server_record *)malloc(sizeof(*s)); - - 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)); - } + if (lp_wins_support()) + { + /* we are a WINS server. */ + add_netbios_entry(name,type,nb_flags,0,SELF,myip); + } + else + { + struct in_addr ip; + ip = ipzero; - return(s); + queue_netbios_pkt_wins(ClientNMB,NMB_REG,NAME_REGISTER, + name, type, nb_flags, + False, True, ip); + } } /**************************************************************************** add the magic samba names, useful for finding samba servers **************************************************************************/ -static void add_my_names(void) +void add_my_names(void) { - struct in_addr ip; - - ip = *interpret_addr2("0.0.0.0"); - - add_host_entry(myname,0x20,True,0,SELF,ip); - add_host_entry(myname,0x0,True,0,SELF,ip); - add_host_entry(myname,0x1f,True,0,SELF,ip); /* used for chat?? */ - add_host_entry(myname,0x3,True,0,SELF,ip); /* used for winpopup */ - - if (!domainlist) - add_domain_entry(lp_workgroup(),bcast_ip); - add_server_entry(myname, - ServerType, - 0,ServerComment,True); - - add_host_entry("__SAMBA__",0x20,True,0,SELF,ip); - add_host_entry("__SAMBA__",0x0,True,0,SELF,ip); - - if (lp_preferred_master()) { - DEBUG(3,("Preferred master startup\n")); - needelection = True; - ElectionCriterion |= (1<<3); - } + struct in_addr ip; - 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; + ip = ipzero; - 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)); + add_netbios_entry(myname,0x20,NB_ACTIVE,0,SELF,ip); + add_netbios_entry(myname,0x03,NB_ACTIVE,0,SELF,ip); + add_netbios_entry(myname,0x00,NB_ACTIVE,0,SELF,ip); + add_netbios_entry(myname,0x1f,NB_ACTIVE,0,SELF,ip); + add_netbios_entry("*",0x01,NB_ACTIVE,0,SELF,ip); + add_netbios_entry("__SAMBA__",0x20,NB_ACTIVE,0,SELF,ip); + add_netbios_entry("__SAMBA__",0x00,NB_ACTIVE,0,SELF,ip); + + if (lp_wins_support()) { + add_netbios_entry(inet_ntoa(myip),0x01,NB_ACTIVE,0,SELF,ip); /* nt as? */ + } } /******************************************************************* - expire old names in the namelist and serverlist + expires old names in the namelist ******************************************************************/ -static void expire_names(void) +void expire_names(time_t t) { - static time_t lastrun=0; - time_t t = time(NULL); - struct name_record *n; - struct name_record *next; - struct server_record *s; - struct server_record *nexts; - - 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; - } - } + struct name_record *n; + struct name_record *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; - } - } + /* 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; + } + } } -/******************************************************************* - delete old names from the namelist - ******************************************************************/ -static void housekeeping(void) +/**************************************************************************** +response for a reg release received +**************************************************************************/ +void response_name_release(struct packet_struct *p) { - time_t t = time(NULL); + struct nmb_packet *nmb = &p->packet.nmb; + char *name = nmb->question.question_name.name; + int type = nmb->question.question_name.name_type; - expire_names(); + DEBUG(4,("response name release received\n")); - /* write out the browse.dat database for smbd to get */ - if (updatedlists) { - write_browse_list(); - updatedlists = False; - } + if (nmb->header.rcode == 0 && nmb->answers->rdata) + { + struct in_addr found_ip; + putip((char*)&found_ip,&nmb->answers->rdata[2]); - { - /* 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; - } - } + if (ip_equal(found_ip, myip)) + { + remove_netbios_name(name,type,SELF,found_ip); + } + } + else + { + DEBUG(1,("name registration for %s rejected!\n", + namestr(&nmb->question.question_name))); + } } /**************************************************************************** - reload the services file - **************************************************************************/ -BOOL reload_services(BOOL test) +reply to a name release +****************************************************************************/ +void reply_name_release(struct packet_struct *p) { - BOOL ret; - extern fstring remote_machine; + struct nmb_packet *nmb = &p->packet.nmb; + struct in_addr ip; + int rcode=0; + int opcode = nmb->header.opcode; + int nb_flags = nmb->additional->rdata[0]; + BOOL bcast = nmb->header.nm_flags.bcast; + struct name_record *n; + char rdata[6]; + + putip((char *)&ip,&nmb->additional->rdata[2]); + + DEBUG(3,("Name release on name %s rcode=%d\n", + namestr(&nmb->question.question_name),rcode)); - strcpy(remote_machine,"nmbd"); + n = find_name_search(&nmb->question.question_name, FIND_GLOBAL, ip); - if (lp_loaded()) - { - pstring fname; - strcpy(fname,lp_configfile()); - if (file_exist(fname,NULL) && !strcsequal(fname,servicesf)) + /* XXXX under what conditions should we reject the removal?? */ + if (n && n->nb_flags == nb_flags && ip_equal(n->ip,ip)) { - strcpy(servicesf,fname); - test = False; - } - } + /* success = True; + rcode = 6; */ - if (test && !lp_file_list_changed()) - return(True); + remove_name(n); + n = NULL; + } - ret = lp_load(servicesf,True); + if (bcast) return; - /* perhaps the config filename is now set */ - if (!test) - reload_services(True); + /*if (success)*/ + { + rdata[0] = nb_flags; + rdata[1] = 0; + putip(&rdata[2],(char *)&ip); + } - return(ret); + /* Send a NAME RELEASE RESPONSE */ + reply_netbios_packet(p,nmb->header.name_trn_id,rcode,opcode, + &nmb->question.question_name, + nmb->question.question_type, + nmb->question.question_class, + 0, + rdata, 6 /*success ? 6 : 0*/); + /* XXXX reject packet never tested: cannot tell what to do */ } - /**************************************************************************** -load a netbios hosts file -****************************************************************************/ -static void load_hosts_file(char *fname) +response for a reg request received +**************************************************************************/ +void response_name_reg(struct packet_struct *p) { - FILE *f = fopen(fname,"r"); - pstring line; - if (!f) { - DEBUG(2,("Can't open lmhosts file %s\n",fname)); - return; - } + struct nmb_packet *nmb = &p->packet.nmb; + char *name = nmb->question.question_name.name; + int type = nmb->question.question_name.name_type; - while (!feof(f)) - { - if (!fgets_slash(line,sizeof(pstring),f)) continue; - - if (*line == '#') continue; + DEBUG(4,("response name registration received!\n")); - { - BOOL group=False; - string ip,name,flags,extra; - char *ptr; - int count = 0; - struct in_addr ipaddr; - enum name_source source = LMHOSTS; + if (nmb->header.rcode == 0 && nmb->answers->rdata) + { + int nb_flags = nmb->answers->rdata[0]; + struct in_addr found_ip; + int ttl = nmb->answers->ttl; + enum name_source source = REGISTER; - *ip = *name = *flags = *extra = 0; + putip((char*)&found_ip,&nmb->answers->rdata[2]); - ptr = line; + if (ip_equal(found_ip, myip)) source = SELF; - 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; + add_netbios_entry(name,type,nb_flags,ttl,source,found_ip); + } + else + { + DEBUG(1,("name registration for %s rejected!\n", + namestr(&nmb->question.question_name))); + } +} - if (count <= 0) continue; - if (count > 0 && count < 2) - { - DEBUG(0,("Ill formed hosts line [%s]\n",line)); - continue; - } +/**************************************************************************** +reply to a reg request +**************************************************************************/ +void reply_name_reg(struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct nmb_name *question = &nmb->question.question_name; + char *qname = nmb->question.question_name.name; + int name_type = nmb->question.question_name.name_type; + + BOOL bcast = nmb->header.nm_flags.bcast; + + int ttl = GET_TTL(nmb->additional->ttl); + int nb_flags = nmb->additional->rdata[0]; + BOOL group = (nb_flags&0x80); + int rcode = 0; + int opcode = nmb->header.opcode; + struct name_record *n = NULL; + int success = True; + char rdata[6]; + struct in_addr ip, from_ip; + + putip((char *)&from_ip,&nmb->additional->rdata[2]); + ip = from_ip; + + DEBUG(3,("Name registration for name %s at %s rcode=%d\n", + namestr(question),inet_ntoa(ip),rcode)); + + if (group) + { + /* apparently we should return 255.255.255.255 for group queries + (email from MS) */ + ip = *interpret_addr2("255.255.255.255"); + } - if (strchr(flags,'G') || strchr(flags,'S')) - group = True; + /* see if the name already exists */ + n = find_name_search(question, FIND_GLOBAL, from_ip); - if (strchr(flags,'M') && !group) { - source = SELF; - strcpy(myname,name); + if (n) + { + if (!group && !ip_equal(ip,n->ip) && question->name_type != 0x3) + { + if (n->source == SELF) + { + rcode = 6; + success = False; + } + else + { + n->ip = ip; + n->death_time = ttl?p->timestamp+ttl*3:0; + DEBUG(3,("%s changed owner to %s\n", + namestr(&n->name),inet_ntoa(n->ip))); + } + } + else + { + /* refresh the name */ + if (n->source != SELF) + { + n->death_time = ttl?p->timestamp + ttl*3:0; + } + } + } + else + { + /* add the name to our subnet/name database */ + n = add_netbios_entry(qname,name_type,nb_flags,ttl,REGISTER,ip); } - ipaddr = *interpret_addr2(ip); + if (bcast) return; + + update_from_reg(nmb->question.question_name.name, + nmb->question.question_name.name_type, from_ip); - if (group) { - add_domain_entry(name,ipaddr); - } else { - add_host_entry(name,0x20,True,0,source,ipaddr); + /* XXXX don't know how to reject a name register: stick info in anyway + and guess that it doesn't matter if info is there! */ + /*if (success)*/ + { + rdata[0] = nb_flags; + rdata[1] = 0; + putip(&rdata[2],(char *)&ip); } - } - } - fclose(f); + /* Send a NAME REGISTRATION RESPONSE */ + reply_netbios_packet(p,nmb->header.name_trn_id,rcode,opcode, + &nmb->question.question_name, + nmb->question.question_type, + nmb->question.question_class, + ttl, + rdata, 6 /*success ? 6 : 0*/); } -/******************************************************************* - 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) +reply to a name status query +****************************************************************************/ +void reply_name_status(struct packet_struct *p) { - pstring outbuf; - char *p; - - DEBUG(2,("Sending election to %s for workgroup %s\n", - inet_ntoa(bcast_ip),group)); - - bzero(outbuf,sizeof(outbuf)); - p = outbuf; - CVAL(p,0) = 8; /* election */ - p++; - - CVAL(p,0) = ELECTION_VERSION; - SIVAL(p,1,criterion); - SIVAL(p,5,timeup*1000); /* ms - despite the spec */ - p += 13; - strcpy(p,name); - strupper(p); - p = skip_string(p,1); - - send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf), - name,group,0,0x1e,bcast_ip,myip); -} + struct nmb_packet *nmb = &p->packet.nmb; + char *qname = nmb->question.question_name.name; + int ques_type = nmb->question.question_name.name_type; + BOOL wildcard = (qname[0] == '*'); + char rdata[MAX_DGRAM_SIZE]; + char *countptr, *buf; + int count, names_added; + struct name_record *n; + + DEBUG(3,("Name status for name %s %s\n", + namestr(&nmb->question.question_name), inet_ntoa(p->ip))); + + /* find a name: if it's a wildcard, search the entire database. + if not, search for source SELF names only */ + n = find_name_search(&nmb->question.question_name, + wildcard ? FIND_GLOBAL : FIND_SELF, p->ip); + + if (!wildcard && (!n || n->source != SELF)) return; + + for (count=0, n = namelist ; n; n = n->next) + { + int name_type = n->name.name_type; + if (n->source != SELF) continue; -/**************************************************************************** - 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); -} + if (name_type >= 0x1b && name_type <= 0x20 && + ques_type >= 0x1b && ques_type <= 0x20) + { + if (!strequal(qname, n->name.name)) continue; + } + count++; + } -/******************************************************************* - 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; - } - } + /* XXXX hack, we should calculate exactly how many will fit */ + count = MIN(count,(sizeof(rdata) - 64) / 18); - add_server_entry(PrimaryGroup,domain_type,0,myname,True); - add_server_entry(myname,ServerType,0,ServerComment,True); + countptr = buf = rdata; + buf += 1; - announce_request(PrimaryGroup); + names_added = 0; - needannounce = True; -} + for (n = namelist ; n && count >= 0; n = n->next) + { + int name_type = n->name.name_type; + if (n->source != SELF) continue; -/******************************************************************* - unbecome the master browser - ******************************************************************/ -static void become_nonmaster(void) -{ - struct name_record *n; - struct nmb_name nn; + /* start with first bit of putting info in buffer: the name */ - DEBUG(2,("Becoming non-master for %s\n",PrimaryGroup)); + bzero(buf,18); + StrnCpy(buf,n->name.name,15); + strupper(buf); - ServerType &= ~SV_TYPE_MASTER_BROWSER; - ServerType &= ~SV_TYPE_DOMAIN_CTRL; - ServerType &= ~SV_TYPE_DOMAIN_MASTER; + /* now check if we want to exclude other workgroup names + from the response. if we don't exclude them, windows clients + get confused and will respond with an error for NET VIEW */ - ElectionCriterion &= ~0x4; + if (name_type >= 0x1b && name_type <= 0x20 && + ques_type >= 0x1b && ques_type <= 0x20) + { + if (!strequal(qname, n->name.name)) continue; + } - make_nmb_name(&nn,PrimaryGroup,0x1d,scope); - n = find_name(&nn); - if (n && n->source == SELF) remove_name(n); + /* carry on putting name info in buffer */ - make_nmb_name(&nn,PrimaryGroup,0x1b,scope); - n = find_name(&nn); - if (n && n->source == SELF) remove_name(n); + buf[15] = name_type; + buf[16] = n->nb_flags; - make_nmb_name(&nn,MSBROWSE,1,scope); - n = find_name(&nn); - if (n && n->source == SELF) remove_name(n); -} + buf += 18; + count--; + names_added++; + } -/******************************************************************* - run the election - ******************************************************************/ -static void run_election(void) -{ - time_t t = time(NULL); - static time_t lastime = 0; + if (count < 0) + { + DEBUG(3, (("too many names: missing a few!\n"))); + } - if (!PrimaryGroup[0] || !RunningElection) return; + SCVAL(countptr,0,names_added); - /* send election packets once a second */ - if (lastime && - t-lastime <= 0) return; + /* 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); + } - lastime = t; + SIVAL(buf,46,0xFFB8E5); /* undocumented - used by NT */ - send_election(PrimaryGroup,ElectionCriterion,t-StartupTime,myname); + buf += 64; - if (ElectionCount++ < 4) return; - - /* I won! now what :-) */ - RunningElection = False; - DEBUG(2,(">>> Won election on %s <<<\n",PrimaryGroup)); - become_master(); + /* Send a POSITIVE NAME STATUS RESPONSE */ + reply_netbios_packet(p,nmb->header.name_trn_id,0,0, + &nmb->question.question_name, + nmb->question.question_type, + nmb->question.question_class, + 0, + rdata,PTR_DIFF(buf,rdata)); } -/**************************************************************************** - construct a host announcement unicast - **************************************************************************/ -static void announce_host(struct domain_record *d,char *my_name,char *comment) +/*************************************************************************** +reply to a name query +****************************************************************************/ +struct name_record *search_for_name(struct nmb_name *question, + struct in_addr ip, int Time, int search) { - 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); - } + int name_type = question->name_type; + char *qname = question->name; + BOOL dns_type = name_type == 0x20 || name_type == 0; - /* announce every minute at first then progress to every 12 mins */ - if (d->lastannounce_time && - (t - d->lastannounce_time) < d->announce_interval) - return; + struct name_record *n; - if (d->announce_interval < 12*60) d->announce_interval += 60; - d->lastannounce_time = t; + DEBUG(3,("Search for %s from %s - ", namestr(question), inet_ntoa(ip))); - DEBUG(2,("Sending announcement to %s for workgroup %s\n", - inet_ntoa(d->bcast_ip),d->name)); + /* first look up name in cache */ + n = find_name_search(question,search,ip); - 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); - } + /* now try DNS lookup. */ + if (!n) + { + struct in_addr dns_ip; + unsigned long a; + + /* only do DNS lookups if the query is for type 0x20 or type 0x0 */ + if (!dns_type) + { + DEBUG(3,("types 0x20 0x1b 0x0 only: name not found\n")); + return NULL; + } + + /* look it up with DNS */ + a = interpret_addr(qname); + + putip((char *)&dns_ip,(char *)&a); + + if (!a) + { + /* no luck with DNS. We could possibly recurse here XXXX */ + /* if this isn't a bcast then we should send a negative reply XXXX */ + DEBUG(3,("no recursion\n")); + add_netbios_entry(qname,name_type,NB_ACTIVE,60*60,DNSFAIL,dns_ip); + return NULL; + } + + /* add it to our cache of names. give it 2 hours in the cache */ + n = add_netbios_entry(qname,name_type,NB_ACTIVE,2*60*60,DNS,dns_ip); + + /* failed to add it? yikes! */ + if (!n) return NULL; + } - 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); - } -} + /* is our entry already dead? */ + if (n->death_time) + { + if (n->death_time < Time) return False; + } + /* it may have been an earlier failure */ + if (n->source == DNSFAIL) + { + DEBUG(3,("DNSFAIL\n")); + return NULL; + } -/**************************************************************************** - send a announce request to the local net - **************************************************************************/ -static void announce_request(char *group) -{ - pstring outbuf; - char *p; + DEBUG(3,("OK %s\n",inet_ntoa(n->ip))); - DEBUG(2,("Sending announce request to %s for workgroup %s\n", - inet_ntoa(bcast_ip),group)); + return n; +} - bzero(outbuf,sizeof(outbuf)); - p = outbuf; - CVAL(p,0) = 2; /* announce request */ - p++; +/* XXXX i think we should only do this if we are a WINS proxy + if (!n && bcast) + { + // now try look up the name at the primary domain controller + if (*lp_domain_controller()) + { + struct in_addr dom_ip; + dom_ip = *interpret_addr2(lp_domain_controller()); + + if (!zero_ip(dom_ip)) + { + struct in_addr found_ip; + + // initiate a netbios query to the PDC + queue_netbios_packet(ClientNMB,NMB_QUERY,NAME_CONFIRM_QUERY, + question->name, question->name_type, 0, + False, True, dom_ip, id); + return; + } + } + } +*/ - CVAL(p,0) = 0; /* flags?? */ - p++; - StrnCpy(p,myname,16); - strupper(p); - p = skip_string(p,1); +/*************************************************************************** +reply to a name query. - send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf), - myname,group,0,0,bcast_ip,myip); -} +with broadcast name queries: -/**************************************************************************** - 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; + - only reply if the query is for one of YOUR names. all other machines on + the network will be doing the same thing (that is, only replying to a + broadcast query if they own it) + NOTE: broadcast name queries should only be sent out by a machine + if they HAVEN'T been configured to use WINS. this is generally bad news + in a wide area tcp/ip network and should be rectified by the systems + administrator. USE WINS! :-) + - the exception to this is if the query is for a Primary Domain Controller + type name (0x1b), in which case, a reply is sent. - if (strequal(domain_controller(),myname)) return; + - NEVER send a negative response to a broadcast query. no-one else will! - if (!AM_MASTER || (last && (t-last < 10*60))) return; - last = t; +with directed name queries: - 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 + - if you are the WINS server, you are expected to ****************************************************************************/ -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) +extern 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; - 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))); + struct nmb_packet *nmb = &p->packet.nmb; + struct nmb_name *question = &nmb->question.question_name; + int name_type = question->name_type; + BOOL dns_type = name_type == 0x20 || name_type == 0; + BOOL bcast = nmb->header.nm_flags.bcast; + int ttl=0; + int rcode = 0; + int nb_flags = 0; + struct in_addr retip; + char rdata[6]; + + struct in_addr gp_ip = *interpret_addr2("255.255.255.255"); + BOOL success = True; + + struct name_record *n; + enum name_search search = dns_type || name_type == 0x1b ? + FIND_GLOBAL : FIND_SELF; + + DEBUG(3,("Name query ")); + + if ((n = search_for_name(question,p->ip,p->timestamp, search))) + { + /* don't respond to broadcast queries unless the query is for + a name we own or it is for a Primary Domain Controller name */ + if (bcast && n->source != SELF && name_type != 0x1b) + { + /* never reply with a negative response to broadcast queries */ + return; + } + + /* name is directed query, or it's self, or it's a PDC type name */ + ttl = n->death_time - p->timestamp; + retip = n->ip; + nb_flags = n->nb_flags; + } + else + { + if (bcast) return; /* never reply negative response to bcasts */ + success = False; } - } 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 */ + /* if asking for a group name (type 0x1e) return 255.255.255.255 */ + if (ip_equal(retip, gp_ip) && name_type == 0x1e) retip = gp_ip; - - 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 */ + /* 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; - buf += 64; + if (success) + { + rcode = 0; + DEBUG(3,("OK %s\n",inet_ntoa(retip))); + } + else + { + rcode = 3; + DEBUG(3,("UNKNOWN\n")); + } - nmb2->answers->rdlength = PTR_DIFF(buf,&nmb2->answers->rdata[0]); + if (success) + { + rdata[0] = nb_flags; + rdata[1] = 0; + putip(&rdata[2],(char *)&retip); + } - send_packet(&p2); + reply_netbios_packet(p,nmb->header.name_trn_id,rcode,0, + &nmb->question.question_name, + nmb->question.question_type, + nmb->question.question_class, + ttl, + rdata, success ? 6 : 0); } - /**************************************************************************** -reply to a name query +response from 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; - } - - if (!lp_proxy_name_resolution() && n->source != SELF) { - DEBUG(3,("no proxy resolution\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) +static void response_netbios_packet(struct packet_struct *p) { - 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; -} + struct nmb_packet *nmb = &p->packet.nmb; + struct nmb_name *question = &nmb->question.question_name; + char *qname = question->name; + BOOL bcast = nmb->header.nm_flags.bcast; + struct name_response_record *n; -/**************************************************************************** - 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) + if (nmb->answers == NULL) { - switch (nmb->question.question_type) - { - case 0x20: - reply_name_query(p); - break; - - case 0x21: - reply_name_status(p); - break; - } - return; + DEBUG(3,("NMB packet response from %s (bcast=%s) - UNKNOWN\n", + inet_ntoa(p->ip), + BOOLSTR(bcast))); + 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(); - } -} + if (nmb->answers->rr_type == NMB_STATUS) + { + DEBUG(3,("Name status ")); + } -/**************************************************************************** - 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 (nmb->answers->rr_type == NMB_QUERY) + { + DEBUG(3,("Name query ")); + } - if (isdaemon) - ClientNMB = open_socket_in(SOCK_DGRAM, port,0); - else - ClientNMB = 0; + if (nmb->answers->rr_type == NMB_REG) + { + DEBUG(3,("Name registration ")); + } - ClientDGRAM = open_socket_in(SOCK_DGRAM,DGRAM_PORT,3); + if (nmb->answers->rr_type == NMB_REL) + { + DEBUG(3,("Name release ")); + } - if (ClientNMB == -1) - return(False); + DEBUG(3,("response for %s from %s (bcast=%s)\n", + namestr(&nmb->answers->rr_name), + inet_ntoa(p->ip), + BOOLSTR(bcast))); - signal(SIGPIPE, SIGNAL_CAST sig_pipe); + if (!(n = find_name_query(nmb->header.name_trn_id))) + { + DEBUG(3,("unknown response (received too late or from nmblookup?)\n")); + return; + } - set_socket_options(ClientNMB,"SO_BROADCAST"); - set_socket_options(ClientDGRAM,"SO_BROADCAST"); + n->num_msgs++; /* count number of responses received */ - DEBUG(3, ("Socket opened.\n")); - return True; + switch (n->cmd_type) + { + case MASTER_SERVER_CHECK : DEBUG(4,("MASTER_SVR_CHECK\n")); break; + case SERVER_CHECK : DEBUG(4,("SERVER_CHECK\n")); break; + case FIND_MASTER : DEBUG(4,("FIND_MASTER\n")); break; + case NAME_STATUS_MASTER_CHECK: DEBUG(4,("NAME_STAT_MST_CHK\n")); break; + case NAME_STATUS_CHECK : DEBUG(4,("NAME_STATUS_CHECK\n")); break; + case CHECK_MASTER : DEBUG(4,("CHECK_MASTER\n")); break; + case NAME_CONFIRM_QUERY : DEBUG(4,("NAME_CONFIRM_QUERY\n")); break; + default: break; + } + switch (n->cmd_type) + { + case MASTER_SERVER_CHECK: + case SERVER_CHECK: + case FIND_MASTER: + { + if (nmb->answers->rr_type == NMB_QUERY) + { + enum cmd_type cmd = (n->cmd_type == MASTER_SERVER_CHECK) ? + NAME_STATUS_MASTER_CHECK : + NAME_STATUS_CHECK; + if (n->num_msgs > 1 && !strequal(qname,n->name.name)) + { + /* one subnet, one master browser per workgroup */ + /* XXXX force an election? */ + DEBUG(1,("more than one master browser replied!\n")); + } + + /* initiate a name status check on the server that replied */ + queue_netbios_packet(ClientNMB,NMB_STATUS, cmd, + nmb->answers->rr_name.name, + nmb->answers->rr_name.name_type,0, + False,False,n->to_ip); + } + else + { + DEBUG(1,("Name query reply has wrong answer rr_type\n")); + } + break; + } + + case NAME_STATUS_MASTER_CHECK: + case NAME_STATUS_CHECK: + { + if (nmb->answers->rr_type == NMB_STATUS) + { + /* NMB_STATUS arrives: contains the workgroup name + and server name we require */ + struct nmb_name name; + fstring serv_name; + + if (interpret_node_status(nmb->answers->rdata, + &name,0x1d,serv_name,n->to_ip)) + { + if (*serv_name) + { + sync_server(n->cmd_type,serv_name, + name.name,name.name_type, + n->to_ip); + } + } + else + { + DEBUG(1,("No 0x1d name type in interpret_node_status()\n")); + } + } + else + { + DEBUG(1,("Name status reply has wrong answer rr_type\n")); + } + break; + } + + case CHECK_MASTER: + { + /* no action required here. it's when NO responses are received + that we need to do something (see expire_name_query_entries) */ + + DEBUG(4, ("Master browser exists for %s at %s\n", + namestr(&n->name), + inet_ntoa(n->to_ip))); + if (n->num_msgs > 1) + { + DEBUG(1,("more than one master browser!\n")); + } + if (nmb->answers->rr_type != NMB_QUERY) + { + DEBUG(1,("Name query reply has wrong answer rr_type\n")); + } + break; + } + case NAME_CONFIRM_QUERY: + { + DEBUG(4, ("Name query at WINS server: %s at %s - ", + namestr(&n->name), + inet_ntoa(n->to_ip))); + if (nmb->header.rcode == 0 && nmb->answers->rdata) + { + int nb_flags = nmb->answers->rdata[0]; + struct in_addr found_ip; + putip((char*)&found_ip,&nmb->answers->rdata[2]); + + DEBUG(4, (" OK: %s\n", inet_ntoa(found_ip))); + add_netbios_entry(nmb->answers->rr_name.name, + nmb->answers->rr_name.name_type, + nb_flags,GET_TTL(0),STATUS_QUERY,found_ip); + } + else + { + DEBUG(4, (" NEGATIVE RESPONSE\n")); + } + + break; + } + default: + { + DEBUG(0,("unknown command received in response_netbios_packet\n")); + break; + } + } } -/******************************************************************* - 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 ) + process a nmb packet + ****************************************************************************/ +void process_nmb(struct packet_struct *p) { - if (!get_myname(myhostname,got_myip?NULL:&myip)) - return(False); - - /* Read the broadcast address from the interface */ - { - struct in_addr ip0,ip1,ip2; - - ip0 = myip; - - 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")); - } - } + struct nmb_packet *nmb = &p->packet.nmb; - 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); - } + debug_nmb_packet(p); - return True; -} + switch (nmb->header.opcode) + { + case 5: + case 8: + case 9: + { + if (nmb->header.qdcount==0 || nmb->header.arcount==0) break; + if (nmb->header.response) + response_name_reg(p); + else + reply_name_reg(p); + break; + } + + case 0: + { + if (nmb->header.response) + { + switch (nmb->question.question_type) + { + case 0x0: + { + response_netbios_packet(p); + break; + } + } + return; + } + else if (nmb->header.qdcount>0) + { + switch (nmb->question.question_type) + { + case NMB_QUERY: + { + reply_name_query(p); + break; + } + case NMB_STATUS: + { + reply_name_status(p); + break; + } + } + return; + } + break; + } + + case 6: + { + if (nmb->header.qdcount==0 || nmb->header.arcount==0) + { + DEBUG(2,("netbios release packet rejected\n")); + break; + } + + if (nmb->header.response) + response_name_release(p); + else + reply_name_release(p); + break; + } + } -/**************************************************************************** -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); -} -- cgit