X-Git-Url: http://git.samba.org/?p=samba.git;a=blobdiff_plain;f=source3%2Fnameserv.c;h=f1b34482c85dd8fe178a82e4e5b3fe7480a5424a;hp=4cd9b099f0011b84f61cf8bfa4cfa6bdcbc613a8;hb=4f6ad2654f40b1170640876bb15f88a40568fb2e;hpb=a2c1623827406667a4f2f058c24f1d971f6627f8 diff --git a/source3/nameserv.c b/source3/nameserv.c index 4cd9b099f00..f1b34482c85 100644 --- a/source3/nameserv.c +++ b/source3/nameserv.c @@ -2,7 +2,7 @@ Unix SMB/Netbios implementation. Version 1.9. NBT netbios routines and daemon - version 2 - Copyright (C) Andrew Tridgell 1994-1995 + Copyright (C) Andrew Tridgell 1994-1997 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,1040 +18,423 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + Module name: nameserv.c + Revision History: 14 jan 96: lkcl@pires.co.uk added multiple workgroup domain master support + 04 jul 96: lkcl@pires.co.uk + module nameserv contains name server management functions */ #include "includes.h" -#include "loadparm.h" -#include "localnet.h" - -enum name_search { FIND_SELF, FIND_GLOBAL }; +extern int ClientNMB; extern int DEBUGLEVEL; extern pstring scope; -extern BOOL CanRecurse; extern pstring myname; +extern fstring myworkgroup; extern struct in_addr ipzero; +extern struct in_addr wins_ip; -/* netbios names database */ -struct name_record *namelist; - -#define GET_TTL(ttl) ((ttl)?MIN(ttl,lp_max_ttl()):lp_max_ttl()) +extern struct subnet_record *subnetlist; +extern uint16 nb_type; /* samba's NetBIOS type */ /**************************************************************************** - 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); + remove an entry from the name list - return(strequal(n1->name,n2->name) && strequal(n1->scope,n2->scope)); -} + note: the name will _always_ be removed + XXXX at present, the name is removed _even_ if a WINS server says keep it. -/**************************************************************************** - add a netbios name into the namelist - **************************************************************************/ -static void add_name(struct name_record *n) + If direct is True then the name being removed must have been a direct name + add. This is done for special names such as DOMAIN<1b>. Just delete it + without any network release traffic. + + ****************************************************************************/ +void remove_name_entry(struct subnet_record *d, char *name,int type, BOOL direct) { - struct name_record *n2; + /* XXXX BUG: if samba is offering WINS support, it should still broadcast + a de-registration packet to the local subnet before removing the + name from its local-subnet name database. */ - if (!namelist) - { - namelist = n; - n->prev = NULL; - n->next = NULL; - return; - } + int search = FIND_SELF; + struct name_record n; + struct name_record *n2=NULL; + + make_nmb_name(&n.name,name,type,scope); - for (n2 = namelist; n2->next; n2 = n2->next) ; + if(d == wins_subnet) + search |= FIND_WINS; + else + search |= FIND_LOCAL; - n2->next = n; - n->next = NULL; - n->prev = n2; -} + if ((n2 = find_name_search(&d, &n.name, search, ipzero))) + { + /* check name isn't already being de-registered */ + if (NAME_DEREG(n2->ip_flgs[0].nb_flags)) + return; -/**************************************************************************** - remove a name from the namelist. The pointer must be an element just - retrieved - **************************************************************************/ -void remove_name(struct name_record *n) -{ - struct name_record *nlist = namelist; + /* mark the name as in the process of deletion. */ + n2->ip_flgs[0].nb_flags &= NB_DEREG; + } - while (nlist && nlist != n) nlist = nlist->next; + if (!n2) return; - if (nlist) + /* Only remove names with non-zero death times. */ + if(n2->death_time == 0) { - if (nlist->next) nlist->next->prev = nlist->prev; - if (nlist->prev) nlist->prev->next = nlist->next; - free(nlist); + DEBUG(5,("remove_name_entry: Name %s(%d) has zero ttl - not removing.\n", + name, type)); + return; } -} -/**************************************************************************** - 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_search(struct nmb_name *name, enum name_search search, - struct in_addr ip) -{ - 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; - } - } - } + /* remove the name immediately. even if the spec says we should + first try to release them, this is too dangerous with our current + name structures as otherwise we will end up replying to names we + don't really own */ + remove_netbios_name(d,name,type,SELF,n2->ip_flgs[0].ip); - return NULL; + if (ip_equal(d->bcast_ip, wins_ip)) + { + if (!lp_wins_support() && !direct) + { + /* not a WINS server: we have to release them on the network */ + queue_netbios_pkt_wins(ClientNMB,NMB_REL,NAME_RELEASE, + name, type, 0, 0,0,NULL,NULL, + False, True, ipzero, ipzero); + } + } + else + { + if(!direct) + /* local interface: release them on the network */ + queue_netbios_packet(d,ClientNMB,NMB_REL,NAME_RELEASE, + name, type, 0, 0,0,NULL,NULL, + True, False, d->bcast_ip, d->bcast_ip); + } } /**************************************************************************** - dump a copy of the name table - **************************************************************************/ -void dump_names(void) -{ - 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)); - } -} - + add an entry to the name list + If the direct BOOL is set then no network traffic is done for the add - it + is just blasted into the subnet entry with a zero TTL - it will not + expire and has not been legitimately claimed. This is *only* done if + we are a WINS server or for a special name such as DOMAIN<1b>. + + big note: our name will _always_ be added (if there are no objections). + it's just a matter of when this will be done (e.g after a time-out). -/**************************************************************************** - remove an entry from the name list ****************************************************************************/ -void remove_netbios_name(char *name,int type, enum name_source source, - struct in_addr ip) +void add_my_name_entry(struct subnet_record *d,char *name,int type,int nb_flags, BOOL direct) { - struct nmb_name nn; - struct name_record *n; - - make_nmb_name(&nn, name, type, scope); - n = find_name_search(&nn, FIND_GLOBAL, ip); - - if (n && n->source == source) remove_name(n); -} - + BOOL re_reg = False; + struct nmb_name n; -/**************************************************************************** - add an entry to the name list - ****************************************************************************/ -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; + if (!d) return; - n = (struct name_record *)malloc(sizeof(*n)); - if (!n) return(NULL); + /* not that it particularly matters, but if the SELF name already exists, + it must be re-registered, rather than just registered */ - bzero((char *)n,sizeof(*n)); + make_nmb_name(&n, name, type, scope); + if (find_name(d->namelist, &n, SELF)) + re_reg = True; - make_nmb_name(&n->name,name,type,scope); + /* XXXX BUG: if samba is offering WINS support, it should still add the + name entry to a local-subnet name database. see rfc1001.txt 15.1.1 p28 + regarding the point about M-nodes. */ - if ((n2 = find_name_search(&n->name, FIND_GLOBAL, ip))) + if (ip_equal(d->bcast_ip, wins_ip)) { - free(n); - n = n2; + if (lp_wins_support() || direct) + { + /* we are a WINS server. */ + if(lp_wins_support()) + DEBUG(4,("add_my_name_entry: samba as WINS server adding: ")); + else + DEBUG(4,("add_my_name_entry: direct name entry : adding: ")); + + /* this will call add_netbios_entry() */ + name_register_work(d, name, type, nb_flags,0, ipzero, False); + } + else + { + DEBUG(4,("add_my_name_entry registering name %s with WINS server.\n", + name)); + + /* a time-to-live allows us to refresh this name with the WINS server. */ + queue_netbios_pkt_wins(ClientNMB, + re_reg ? NMB_REG_REFRESH : NMB_REG, NAME_REGISTER, + name, type, nb_flags, GET_TTL(0),0,NULL,NULL, + False, True, ipzero, ipzero); + } + } + else + { + if(direct) + { + /* Just enter the name to be the ip address of the subnet + via name_register_work to ensure all side effects are done. + */ + DEBUG(4,("add_my_name_entry: direct name entry : adding: ")); + /* this will call add_netbios_entry() */ + name_register_work(d, name, type, nb_flags,0, d->myip, False); + } + else + { + /* broadcast the packet, but it comes from ipzero */ + queue_netbios_packet(d,ClientNMB, + re_reg ? NMB_REG_REFRESH : NMB_REG, NAME_REGISTER, + name, type, nb_flags, GET_TTL(0),0,NULL,NULL, + True, False, d->bcast_ip, ipzero); + } } - - if (ttl) n->death_time = time(NULL)+ttl*3; - n->ip = ip; - n->nb_flags = nb_flags; - n->source = source; - - if (!n2) add_name(n); - - 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); -} - - -/**************************************************************************** - remove an entry from the name list - ****************************************************************************/ -void remove_name_entry(char *name,int type) -{ - if (lp_wins_support()) - { - /* we are a WINS server. */ - remove_netbios_name(name,type,SELF,myip); - } - else - { - struct in_addr ip; - ip = ipzero; - - queue_netbios_pkt_wins(ClientNMB,NMB_REL,NAME_RELEASE, - name, type, 0, - False, True, ip); - } } /**************************************************************************** - add an entry to the name list - ****************************************************************************/ -void add_name_entry(char *name,int type,int nb_flags) -{ - 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; + add the domain logon server and domain master browser names - queue_netbios_pkt_wins(ClientNMB,NMB_REG,NAME_REGISTER, - name, type, nb_flags, - False, True, ip); - } -} + this code was written so that several samba servers can co-operate in + sharing the task of (one server) being a domain master, and of being + domain logon servers. - -/**************************************************************************** - add the magic samba names, useful for finding samba servers **************************************************************************/ -void add_my_names(void) +void add_domain_names(time_t t) { - struct in_addr ip; - - ip = ipzero; - - 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? */ - } -} - -/******************************************************************* - expires old names in the namelist - ******************************************************************/ -void expire_names(time_t t) -{ - struct name_record *n; - struct name_record *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; + static time_t lastrun = 0; + struct subnet_record *d; + struct work_record *work; + struct nmb_name n; - 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; - } - } -} + if (lastrun != 0 && t < lastrun + CHECK_TIME_ADD_DOM_NAMES * 60) return; + lastrun = t; + for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_INCLUDING_WINS(d)) + { + work = find_workgroupstruct(d, myworkgroup, False); + if (lp_domain_logons() && work && work->log_state == LOGON_NONE) + { + make_nmb_name(&n,myworkgroup,0x1c,scope); + if (!find_name(d->namelist, &n, FIND_SELF)) + { + /* logon servers are group names - we don't expect this to fail. */ + DEBUG(0,("%s attempting to become logon server for %s %s\n", + timestring(), myworkgroup, inet_ntoa(d->bcast_ip))); + become_logon_server(d, work); + } + } + } -/**************************************************************************** -response for a reg release received -**************************************************************************/ -void response_name_release(struct packet_struct *p) -{ - struct nmb_packet *nmb = &p->packet.nmb; - char *name = nmb->question.question_name.name; - int type = nmb->question.question_name.name_type; - - DEBUG(4,("response name release received\n")); - - if (nmb->header.rcode == 0 && nmb->answers->rdata) - { - struct in_addr found_ip; - putip((char*)&found_ip,&nmb->answers->rdata[2]); - - 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))); - } + for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_INCLUDING_WINS(d)) + { + work = find_workgroupstruct(d, myworkgroup, True); + + if (lp_domain_master() && work && work->dom_state == DOMAIN_NONE) + { + make_nmb_name(&n,myworkgroup,0x1b,scope); + if (!find_name(d->namelist, &n, FIND_SELF)) + { + DEBUG(0,("%s add_domain_names: attempting to become domain master \ +browser on workgroup %s %s\n", + timestring(), myworkgroup, inet_ntoa(d->bcast_ip))); + + if(d == wins_subnet) + { + if (lp_wins_support()) + { + /* use the wins server's capabilities (indirectly). if + someone has already registered the domain<1b> name with + the WINS server, then the WINS server's job is to _check_ + that the owner still wants it, before giving it away. + */ + + DEBUG(1,("%s initiating becoming domain master for %s\n", + timestring(), myworkgroup)); + become_domain_master(d, work); + } + else + { + /* send out a query to establish whether there's a + domain controller on the WINS subnet. if not, + we can become a domain controller. + it's only polite that we check, before claiming the + NetBIOS name 0x1b. + */ + + DEBUG(0,("add_domain_names:querying WINS for domain master \ +on workgroup %s\n", myworkgroup)); + + queue_netbios_pkt_wins(ClientNMB,NMB_QUERY,NAME_QUERY_DOMAIN, + myworkgroup, 0x1b, + 0, 0,0,NULL,NULL, + False, True, ipzero, ipzero); + } + } + else + { + DEBUG(0,("add_domain_names:querying subnet %s for domain master \ +on workgroup %s\n", inet_ntoa(d->bcast_ip), myworkgroup)); + queue_netbios_packet(d,ClientNMB,NMB_QUERY,NAME_QUERY_DOMAIN, + myworkgroup, 0x1b, + 0, 0,0,NULL,NULL, + True, False, + d->bcast_ip, d->bcast_ip); + } + } + } + } } /**************************************************************************** -reply to a name release -****************************************************************************/ -void reply_name_release(struct packet_struct *p) + add the magic samba names, useful for finding samba servers + **************************************************************************/ +void add_my_names(void) { - 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)); - - n = find_name_search(&nmb->question.question_name, FIND_GLOBAL, ip); + struct subnet_record *d; + /* each subnet entry, including WINS pseudo-subnet, has SELF names */ - /* XXXX under what conditions should we reject the removal?? */ - if (n && n->nb_flags == nb_flags && ip_equal(n->ip,ip)) - { - /* success = True; - rcode = 6; */ - - remove_name(n); - n = NULL; - } + /* XXXX if there was a transport layer added to samba (ipx/spx etc) then + there would be yet _another_ for-loop, this time on the transport type + */ - if (bcast) return; - - /*if (success)*/ - { - rdata[0] = nb_flags; - rdata[1] = 0; - putip(&rdata[2],(char *)&ip); - } - - /* 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 */ + for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_INCLUDING_WINS(d)) + { + BOOL wins = (lp_wins_support() && (d == wins_subnet)); + + add_my_name_entry(d, myname,0x20,nb_type|NB_ACTIVE,False); + add_my_name_entry(d, myname,0x03,nb_type|NB_ACTIVE,False); + add_my_name_entry(d, myname,0x00,nb_type|NB_ACTIVE,False); + + /* these names are added permanently (ttl of zero) and will NOT be + refreshed with the WINS server */ + add_netbios_entry(d,"*",0x0,nb_type|NB_ACTIVE,0,SELF,d->myip,False,wins); + add_netbios_entry(d,"*",0x20,nb_type|NB_ACTIVE,0,SELF,d->myip,False,wins); + add_netbios_entry(d,"__SAMBA__",0x20,nb_type|NB_ACTIVE,0,SELF,d->myip,False,wins); + add_netbios_entry(d,"__SAMBA__",0x00,nb_type|NB_ACTIVE,0,SELF,d->myip,False,wins); + } } /**************************************************************************** -response for a reg request received -**************************************************************************/ -void response_name_reg(struct packet_struct *p) + remove all the samba names... from a WINS server if necessary. + **************************************************************************/ +void remove_my_names() { - struct nmb_packet *nmb = &p->packet.nmb; - char *name = nmb->question.question_name.name; - int type = nmb->question.question_name.name_type; - - DEBUG(4,("response name registration received!\n")); + struct subnet_record *d; - if (nmb->header.rcode == 0 && nmb->answers->rdata) + for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_INCLUDING_WINS(d)) { - int nb_flags = nmb->answers->rdata[0]; - struct in_addr found_ip; - int ttl = nmb->answers->ttl; - enum name_source source = REGISTER; + struct name_record *n, *next; - putip((char*)&found_ip,&nmb->answers->rdata[2]); - - if (ip_equal(found_ip, myip)) source = SELF; - - 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))); - } -} - - -/**************************************************************************** -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"); - } - - /* see if the name already exists */ - n = find_name_search(question, FIND_GLOBAL, from_ip); - - if (n) - { - if (!group && !ip_equal(ip,n->ip) && question->name_type != 0x3) + for (n = d->namelist; n; n = next) { + next = n->next; 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); - } - - if (bcast) return; - - update_from_reg(nmb->question.question_name.name, - nmb->question.question_name.name_type, from_ip); - - /* 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); - } + /* get all SELF names removed from the WINS server's database */ + /* XXXX note: problem occurs if this removes the wrong one! */ - /* 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*/); -} - - -/**************************************************************************** -reply to a name status query -****************************************************************************/ -void reply_name_status(struct packet_struct *p) -{ - 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; - - if (name_type >= 0x1b && name_type <= 0x20 && - ques_type >= 0x1b && ques_type <= 0x20) - { - if (!strequal(qname, n->name.name)) continue; - } - - count++; - } - - /* XXXX hack, we should calculate exactly how many will fit */ - count = MIN(count,(sizeof(rdata) - 64) / 18); - - countptr = buf = rdata; - buf += 1; - - names_added = 0; - - for (n = namelist ; n && count >= 0; n = n->next) - { - int name_type = n->name.name_type; - - if (n->source != SELF) continue; - - /* start with first bit of putting info in buffer: the name */ - - bzero(buf,18); - StrnCpy(buf,n->name.name,15); - strupper(buf); - - /* 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 */ - - if (name_type >= 0x1b && name_type <= 0x20 && - ques_type >= 0x1b && ques_type <= 0x20) - { - if (!strequal(qname, n->name.name)) continue; + remove_name_entry(d,n->name.name, n->name.name_type,False); + } } - - /* carry on putting name info in buffer */ - - buf[15] = name_type; - buf[16] = n->nb_flags; - - buf += 18; - - count--; - names_added++; } - - if (count < 0) - { - DEBUG(3, (("too many names: missing a few!\n"))); - } - - SCVAL(countptr,0,names_added); - - /* 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; - - /* 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)); } -/*************************************************************************** -reply to a name query -****************************************************************************/ -struct name_record *search_for_name(struct nmb_name *question, - struct in_addr ip, int Time, int search) +/******************************************************************* + refresh my own names + ******************************************************************/ +void refresh_my_names(time_t t) { - int name_type = question->name_type; - char *qname = question->name; - BOOL dns_type = name_type == 0x20 || name_type == 0; - - struct name_record *n; - - DEBUG(3,("Search for %s from %s - ", namestr(question), inet_ntoa(ip))); - - /* first look up name in cache */ - n = find_name_search(question,search,ip); - - /* 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; - } - - /* is our entry already dead? */ - if (n->death_time) - { - if (n->death_time < Time) return False; - } + struct subnet_record *d; - /* it may have been an earlier failure */ - if (n->source == DNSFAIL) - { - DEBUG(3,("DNSFAIL\n")); - return NULL; - } - - DEBUG(3,("OK %s\n",inet_ntoa(n->ip))); - - return n; + for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_INCLUDING_WINS(d)) + { + struct name_record *n; + + for (n = d->namelist; n; n = n->next) + { + /* each SELF name has an individual time to be refreshed */ + if (n->source == SELF && n->refresh_time < t && + n->death_time != 0) + { + add_my_name_entry(d,n->name.name,n->name.name_type, + n->ip_flgs[0].nb_flags,False); + /* they get a new lease on life :-) */ + n->death_time += GET_TTL(0); + n->refresh_time += GET_TTL(0); + } + } + } } -/* 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; - } - } - } -*/ -/*************************************************************************** -reply to a name query. - -with broadcast name queries: - - - 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. - - - NEVER send a negative response to a broadcast query. no-one else will! +/******************************************************************* + queries names occasionally. an over-cautious, non-trusting WINS server! -with directed name queries: + this function has been added because nmbd could be restarted. it + is generally a good idea to check all the names that have been + reloaded from file. - - if you are the WINS server, you are expected to -****************************************************************************/ -extern void reply_name_query(struct packet_struct *p) + XXXX which names to poll and which not can be refined at a later date. + ******************************************************************/ +void query_refresh_names(time_t t) { - 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; - } + struct subnet_record *d = wins_subnet; - /* 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; - - /* 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; - - if (success) - { - rcode = 0; - DEBUG(3,("OK %s\n",inet_ntoa(retip))); - } - else - { - rcode = 3; - DEBUG(3,("UNKNOWN\n")); - } + static time_t lasttime = 0; - if (success) - { - rdata[0] = nb_flags; - rdata[1] = 0; - putip(&rdata[2],(char *)&retip); - } + int count = 0; + int name_refresh_time = NAME_POLL_REFRESH_TIME; + int max_count = name_refresh_time * 2 / NAME_POLL_INTERVAL; + if (max_count > 10) max_count = 10; - 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); -} + name_refresh_time = NAME_POLL_INTERVAL * max_count / 2; + /* if (!lp_poll_wins()) return; polling of registered names allowed */ -/**************************************************************************** -response from a name query -****************************************************************************/ -static void response_netbios_packet(struct packet_struct *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; + if (!d) return; - if (nmb->answers == NULL) - { - DEBUG(3,("NMB packet response from %s (bcast=%s) - UNKNOWN\n", - inet_ntoa(p->ip), - BOOLSTR(bcast))); - return; - } + if (!lasttime) lasttime = t; + if (t - lasttime < NAME_POLL_INTERVAL) return; - if (nmb->answers->rr_type == NMB_STATUS) - { - DEBUG(3,("Name status ")); - } + lasttime = time(NULL); - if (nmb->answers->rr_type == NMB_QUERY) + for (n = d->namelist; n; n = n->next) { - DEBUG(3,("Name query ")); - } - - if (nmb->answers->rr_type == NMB_REG) - { - DEBUG(3,("Name registration ")); - } - - if (nmb->answers->rr_type == NMB_REL) - { - DEBUG(3,("Name release ")); - } - - DEBUG(3,("response for %s from %s (bcast=%s)\n", - namestr(&nmb->answers->rr_name), - inet_ntoa(p->ip), - BOOLSTR(bcast))); - - if (!(n = find_name_query(nmb->header.name_trn_id))) - { - DEBUG(3,("unknown response (received too late or from nmblookup?)\n")); - return; - } - - n->num_msgs++; /* count number of responses received */ - - 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; - } + /* only do unique, registered names */ - 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; - } + if (n->source != REGISTER) continue; + if (!NAME_GROUP(n->ip_flgs[0].nb_flags)) continue; - case CHECK_MASTER: + if (n->refresh_time < t) { - /* 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; + DEBUG(3,("Polling name %s\n", namestr(&n->name))); + + queue_netbios_packet(d,ClientNMB,NMB_QUERY,NAME_QUERY_CONFIRM, + n->name.name, n->name.name_type, + 0,0,0,NULL,NULL, + False,False,n->ip_flgs[0].ip,n->ip_flgs[0].ip); + count++; } - 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: + if (count >= max_count) { - DEBUG(0,("unknown command received in response_netbios_packet\n")); - break; - } - } -} - - -/**************************************************************************** - process a nmb packet - ****************************************************************************/ -void process_nmb(struct packet_struct *p) -{ - struct nmb_packet *nmb = &p->packet.nmb; - - debug_nmb_packet(p); - - 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; + /* don't do too many of these at once, but do enough to + cover everyone in the list */ + return; } - 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; - } + /* this name will be checked on again, if it's not removed */ + n->refresh_time += name_refresh_time; } - }