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
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;
}
-
}