summaryrefslogtreecommitdiff
path: root/source3/nameserv.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/nameserv.c')
-rw-r--r--source3/nameserv.c1599
1 files changed, 9 insertions, 1590 deletions
diff --git a/source3/nameserv.c b/source3/nameserv.c
index 6ff2167271..056c943cd6 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-1996
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,462 +18,30 @@
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"
extern int ClientNMB;
-extern int ClientDGRAM;
-
-#define FIND_SELF 0x01
-#define FIND_WINS 0x02
-#define FIND_LOCAL 0x04
extern int DEBUGLEVEL;
extern pstring scope;
-extern BOOL CanRecurse;
extern pstring myname;
extern struct in_addr ipzero;
extern struct in_addr ipgrp;
extern struct subnet_record *subnetlist;
-#define WINS_LIST "wins.dat"
-
-#define GET_TTL(ttl) ((ttl)?MIN(ttl,lp_max_ttl()):lp_max_ttl())
-
-/****************************************************************************
- finds the appropriate subnet structure. directed packets (non-bcast) are
- assumed to come from a point-to-point (P or M node), and so the subnet we
- return in this instance is the WINS 'pseudo-subnet' with ip 255.255.255.255
- ****************************************************************************/
-static struct subnet_record *find_req_subnet(struct in_addr ip, BOOL bcast)
-{
- if (bcast)
- {
- /* identify the subnet the broadcast request came from */
- return find_subnet(*iface_bcast(ip));
- }
- /* find the subnet under the pseudo-ip of 255.255.255.255 */
- return find_subnet(ipgrp);
-}
-
-
-/****************************************************************************
- true if two netbios names are equal
-****************************************************************************/
-static BOOL name_equal(struct nmb_name *n1,struct nmb_name *n2)
-{
- return n1->name_type == n2->name_type &&
- strequal(n1->name ,n2->name ) &&
- strequal(n1->scope,n2->scope);
-}
-
-
-/****************************************************************************
- true if the netbios name is ^1^2__MSBROWSE__^2^1
-
- note: this name is registered if as a master browser or backup browser
- you are responsible for a workgroup (when you announce a domain by
- broadcasting on your local subnet, you announce it as coming from this
- name: see announce_host()).
-
- WINS is configured to ignore this 'special browser name', presumably
- because it's redundant: there's no such thing as an announceable
- domain when dealing with a wide area network and a WINS server.
-
- **************************************************************************/
-BOOL special_browser_name(char *name, int type)
-{
- return strequal(name,MSBROWSE) && type == 0x01;
-}
-
-
-/****************************************************************************
- add a netbios name into the namelist
- **************************************************************************/
-static void add_name(struct subnet_record *d, struct name_record *n)
-{
- struct name_record *n2;
-
- if (!d) return;
-
- if (!d->namelist)
- {
- d->namelist = n;
- n->prev = NULL;
- n->next = NULL;
- return;
- }
-
- for (n2 = d->namelist; n2->next; n2 = n2->next) ;
-
- n2->next = n;
- n->next = NULL;
- n->prev = n2;
-}
-
-
-/****************************************************************************
- remove a name from the namelist. The pointer must be an element just
- retrieved
- **************************************************************************/
-void remove_name(struct subnet_record *d, struct name_record *n)
-{
- struct name_record *nlist;
- if (!d) return;
-
- nlist = d->namelist;
-
- while (nlist && nlist != n) nlist = nlist->next;
-
- if (nlist)
- {
- if (nlist->next) nlist->next->prev = nlist->prev;
- if (nlist->prev) nlist->prev->next = nlist->next;
- free(nlist);
- }
-}
-
-
-/****************************************************************************
- find a name in a namelist.
- **************************************************************************/
-static struct name_record *find_name(struct name_record *n,
- struct nmb_name *name,
- int search, struct in_addr ip)
-{
- struct name_record *ret;
-
- for (ret = n; ret; ret = ret->next)
- {
- if (name_equal(&ret->name,name))
- {
- /* self search: self names only */
- if ((search&FIND_SELF) == FIND_SELF && ret->source != SELF)
- continue;
-
- /* zero ip is either samba's ip or a way of finding a
- name without needing to know the ip address */
- if (zero_ip(ip) || ip_equal(ip, ret->ip))
- {
- return ret;
- }
- }
- }
- return NULL;
-}
-
-
-/****************************************************************************
- find a name in the domain database namelist
- search can be any of:
- FIND_SELF - look exclusively for names the samba server has added for itself
- FIND_LOCAL - look for names in the local subnet record.
- FIND_WINS - look for names in the WINS record
- **************************************************************************/
-static struct name_record *find_name_search(struct subnet_record **d,
- struct nmb_name *name,
- int search, struct in_addr ip)
-{
- if (d == NULL) return NULL; /* bad error! */
-
- if ((search & FIND_LOCAL) == FIND_LOCAL)
- {
- if (*d != NULL)
- {
- return find_name((*d)->namelist, name, search, ip);
- }
- else
- {
- DEBUG(4,("local find_name_search with a NULL subnet pointer\n"));
- return NULL;
- }
- }
-
- if ((search & FIND_WINS) != FIND_WINS) return NULL;
-
- if (*d == NULL)
- {
- /* find WINS subnet record */
- *d = find_subnet(ipgrp);
- }
-
- if (*d == NULL) return NULL;
-
- return find_name((*d)->namelist, name, search, ip);
-}
-
-
-/****************************************************************************
- dump a copy of the name table
- **************************************************************************/
-void dump_names(void)
-{
- struct name_record *n;
- struct subnet_record *d;
- fstring fname, fnamenew;
- time_t t = time(NULL);
-
- FILE *f;
-
- strcpy(fname,lp_lockdir());
- trim_string(fname,NULL,"/");
- strcat(fname,"/");
- strcat(fname,WINS_LIST);
- strcpy(fnamenew,fname);
- strcat(fnamenew,".");
-
- f = fopen(fnamenew,"w");
-
- if (!f)
- {
- DEBUG(4,("Can't open %s - %s\n",fnamenew,strerror(errno)));
- }
-
- DEBUG(3,("Dump of local name table:\n"));
-
- for (d = subnetlist; d; d = d->next)
- for (n = d->namelist; n; n = n->next)
- {
- if (f && ip_equal(d->bcast_ip, ipgrp) && n->source == REGISTER)
- {
- fstring data;
-
- /* XXXX i have little imagination as to how to output nb_flags as
- anything other than as a hexadecimal number :-) */
-
- sprintf(data, "%s#%02x %s %ld %2x",
- n->name.name,n->name.name_type, /* XXXX ignore the scope for now */
- inet_ntoa(n->ip),
- n->death_time,
- n->nb_flags);
- fprintf(f, "%s\n", data);
- }
-
- DEBUG(3,("%15s ", inet_ntoa(d->bcast_ip)));
- DEBUG(3,("%15s ", inet_ntoa(d->mask_ip)));
- DEBUG(3,("%s %15s TTL=%15d NBFLAGS=%2x\n",
- namestr(&n->name),
- inet_ntoa(n->ip),
- n->death_time?n->death_time-t:0,
- n->nb_flags));
- }
-
- fclose(f);
- unlink(fname);
- chmod(fnamenew,0644);
- rename(fnamenew,fname);
-
- DEBUG(3,("Wrote wins database %s\n",fname));
-}
-
-/****************************************************************************
-load a netbios name database file
-****************************************************************************/
-void load_netbios_names(void)
-{
- struct subnet_record *d = find_subnet(ipgrp);
- fstring fname;
-
- FILE *f;
- pstring line;
-
- if (!d) return;
-
- strcpy(fname,lp_lockdir());
- trim_string(fname,NULL,"/");
- strcat(fname,"/");
- strcat(fname,WINS_LIST);
-
- f = fopen(fname,"r");
-
- if (!f) {
- DEBUG(2,("Can't open wins database file %s\n",fname));
- return;
- }
-
- while (!feof(f))
- {
- pstring name_str, ip_str, ttd_str, nb_flags_str;
-
- pstring name;
- int type = 0;
- int nb_flags;
- time_t ttd;
- struct in_addr ipaddr;
-
- enum name_source source;
-
- char *ptr;
- int count = 0;
-
- char *p;
-
- if (!fgets_slash(line,sizeof(pstring),f)) continue;
-
- if (*line == '#') continue;
-
- ptr = line;
-
- if (next_token(&ptr,name_str ,NULL)) ++count;
- if (next_token(&ptr,ip_str ,NULL)) ++count;
- if (next_token(&ptr,ttd_str ,NULL)) ++count;
- if (next_token(&ptr,nb_flags_str,NULL)) ++count;
-
- if (count <= 0) continue;
-
- if (count != 4) {
- DEBUG(0,("Ill formed wins line"));
- DEBUG(0,("[%s]: name#type ip nb_flags abs_time\n",line));
- continue;
- }
-
- /* netbios name. # divides the name from the type (hex): netbios#xx */
- strcpy(name,name_str);
-
- p = strchr(name,'#');
-
- if (p) {
- *p = 0;
- sscanf(p+1,"%x",&type);
- }
-
- /* decode the netbios flags (hex) and the time-to-die (seconds) */
- sscanf(nb_flags_str,"%x",&nb_flags);
- sscanf(ttd_str,"%ld",&ttd);
-
- ipaddr = *interpret_addr2(ip_str);
-
- if (ip_equal(ipaddr,ipzero)) {
- source = SELF;
- }
- else
- {
- source = REGISTER;
- }
-
- DEBUG(4, ("add WINS line: %s#%02x %s %ld %2x\n",
- name,type, inet_ntoa(ipaddr), ttd, nb_flags));
-
- /* add all entries that have 60 seconds or more to live */
- if (ttd - 60 < time(NULL) || ttd == 0)
- {
- time_t t = (ttd?ttd-time(NULL):0) / 3;
-
- /* add netbios entry read from the wins.dat file. IF it's ok */
- add_netbios_entry(d,name,type,nb_flags,t,source,ipaddr,True,True);
- }
- }
-
- fclose(f);
-}
-
-
-/****************************************************************************
- remove an entry from the name list
- ****************************************************************************/
-void remove_netbios_name(struct subnet_record *d,
- char *name,int type, enum name_source source,
- struct in_addr ip)
-{
- struct nmb_name nn;
- struct name_record *n;
- int search = FIND_LOCAL;
-
- /* if it's not a special browser name, search the WINS database */
- if (!special_browser_name(name, type))
- search |= FIND_WINS;
-
- make_nmb_name(&nn, name, type, scope);
- n = find_name_search(&d, &nn, search, ip);
-
- if (n && n->source == source) remove_name(d,n);
-}
-
-
-/****************************************************************************
- add an entry to the name list.
-
- this is a multi-purpose function.
-
- it adds samba's own names in to its records on each interface, keeping a
- record of whether it is a master browser, domain master, or WINS server.
-
- it also keeps a record of WINS entries (names of type 0x00, 0x20, 0x03 etc)
-
- ****************************************************************************/
-struct name_record *add_netbios_entry(struct subnet_record *d,
- char *name, int type, int nb_flags,
- int ttl, enum name_source source, struct in_addr ip,
- BOOL new_only,BOOL wins)
-{
- struct name_record *n;
- struct name_record *n2=NULL;
- int search = 0;
- BOOL self = source == SELF;
-
- /* add the name to the WINS list if the name comes from a directed query */
- search |= wins ? FIND_WINS : FIND_LOCAL;
- /* search for SELF names only */
- search |= self ? FIND_SELF : 0;
-
- if (!self)
- {
- if (wins)
- {
- if (special_browser_name(name, type))
- {
- /* XXXX WINS server supposed to ignore special browser names. hm.
- but is a primary domain controller supposed to ignore special
- browser names? luke doesn't think so, but can't test it! :-)
- */
- return NULL;
- }
- }
- else /* !wins */
- {
- /* the only broadcast (non-WINS) names we are adding are ours (SELF) */
- return NULL;
- }
- }
-
- n = (struct name_record *)malloc(sizeof(*n));
- if (!n) return(NULL);
-
- bzero((char *)n,sizeof(*n));
-
- make_nmb_name(&n->name,name,type,scope);
-
- if ((n2 = find_name_search(&d, &n->name, search, new_only?ipzero:ip)))
- {
- free(n);
- if (new_only || (n2->source==SELF && source!=SELF)) return n2;
- n = n2;
- }
-
- if (ttl)
- n->death_time = time(NULL)+ttl*3;
- n->refresh_time = time(NULL)+GET_TTL(ttl);
-
- n->ip = ip;
- n->nb_flags = nb_flags;
- n->source = source;
-
- if (!n2) add_name(d,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
@@ -514,7 +82,7 @@ void remove_name_entry(struct subnet_record *d, char *name,int type)
}
queue_netbios_pkt_wins(d,ClientNMB,NMB_REL,NAME_RELEASE,
name, type, 0, 0,
- False, True, ipzero);
+ False, True, ipzero, ipzero);
}
}
@@ -557,7 +125,7 @@ void add_my_name_entry(struct subnet_record *d,char *name,int type,int nb_flags)
queue_netbios_pkt_wins(d,ClientNMB,
re_reg ? NMB_REG_REFRESH : NMB_REG, NAME_REGISTER,
name, type, nb_flags, GET_TTL(0),
- False, True, ipzero);
+ False, True, ipzero, ipzero);
}
}
@@ -649,6 +217,7 @@ void refresh_my_names(time_t t)
}
}
+
/*******************************************************************
queries names occasionally. an over-cautious, non-trusting WINS server!
@@ -693,7 +262,7 @@ void query_refresh_names(void)
queue_netbios_packet(d,ClientNMB,NMB_QUERY,NAME_QUERY_CONFIRM,
n->name.name, n->name.name_type,
0,0,
- False,False,n->ip);
+ False,False,n->ip,n->ip);
count++;
}
@@ -708,1153 +277,3 @@ void query_refresh_names(void)
n->refresh_time += name_refresh_time;
}
}
-
-
-/*******************************************************************
- expires old names in the namelist
- ******************************************************************/
-void expire_names(time_t t)
-{
- struct name_record *n;
- struct name_record *next;
- struct subnet_record *d;
-
- /* expire old names */
- for (d = subnetlist; d; d = d->next)
- {
- for (n = d->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 (d->namelist == n) d->namelist = n->next;
-
- free(n);
- }
- else
- {
- next = n->next;
- }
- }
- }
-}
-
-
-/****************************************************************************
- response for a reg release received. samba has asked a WINS server if it
- could release a name.
- **************************************************************************/
-void response_name_release(struct subnet_record *d, 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)
- {
- /* IMPORTANT: see expire_netbios_response_entries() */
-
- struct in_addr found_ip;
- putip((char*)&found_ip,&nmb->answers->rdata[2]);
-
- if (ismyip(found_ip))
- {
- remove_netbios_name(d,name,type,SELF,found_ip);
- }
- }
- else
- {
- DEBUG(2,("name release for %s rejected!\n",
- namestr(&nmb->question.question_name)));
-
- /* XXXX do we honestly care if our name release was rejected?
- only if samba is issuing the release on behalf of some out-of-sync
- server. if it's one of samba's SELF names, we don't care. */
- }
-}
-
-
-/****************************************************************************
-reply to a name release
-****************************************************************************/
-void reply_name_release(struct packet_struct *p)
-{
- 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;
- struct subnet_record *d = NULL;
- char rdata[6];
- int search = 0;
-
- putip((char *)&ip,&nmb->additional->rdata[2]);
-
- DEBUG(3,("Name release on name %s rcode=%d\n",
- namestr(&nmb->question.question_name),rcode));
-
- if (!(d = find_req_subnet(p->ip, bcast)))
- {
- DEBUG(3,("response packet: bcast %s not known\n",
- inet_ntoa(p->ip)));
- return;
- }
-
- if (bcast)
- search &= FIND_LOCAL;
- else
- search &= FIND_WINS;
-
- n = find_name_search(&d, &nmb->question.question_name,
- search, ip);
-
- /* XXXX under what conditions should we reject the removal?? */
- if (n && n->nb_flags == nb_flags)
- {
- /* success = True;
- rcode = 6; */
-
- remove_name(d,n);
- n = NULL;
- }
-
- if (bcast) return;
-
- 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,True,
- &nmb->question.question_name,
- nmb->question.question_type,
- nmb->question.question_class,
- 0,
- rdata, 6);
-}
-
-
-/****************************************************************************
-response for a reg request received
-**************************************************************************/
-void response_name_reg(struct subnet_record *d, 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;
- BOOL bcast = nmb->header.nm_flags.bcast;
-
- DEBUG(4,("response name registration received!\n"));
-
- if (nmb->header.rcode == 0 && nmb->answers->rdata)
- {
- /* IMPORTANT: see expire_netbios_response_entries() */
-
- int nb_flags = nmb->answers->rdata[0];
- int ttl = nmb->answers->ttl;
- struct in_addr found_ip;
-
- putip((char*)&found_ip,&nmb->answers->rdata[2]);
-
- name_register_work(d,name,type,nb_flags,ttl,found_ip,bcast);
- }
- else
- {
- DEBUG(1,("name registration for %s rejected!\n",
- namestr(&nmb->question.question_name)));
-
- /* XXXX oh dear. we have problems. must deal with our name having
- been rejected: e.g if it was our GROUP(1d) name, we must unbecome
- a master browser. */
-
- name_unregister_work(d,name,type);
- }
-}
-
-
-/****************************************************************************
-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;
-
- struct nmb_name *reply_name = question;
- char *qname = question->name;
- int name_type = question->name_type;
- int name_class = nmb->question.question_class;
-
- BOOL bcast = nmb->header.nm_flags.bcast;
-
- int ttl = GET_TTL(nmb->additional->ttl);
- int nb_flags = nmb->additional->rdata[0];
- BOOL group = NAME_GROUP(nb_flags);
- int rcode = 0;
- int opcode = nmb->header.opcode;
-
- struct subnet_record *d = NULL;
- struct name_record *n = NULL;
- BOOL success = True;
- BOOL recurse = True; /* true if samba replies yes/no: false if caller */
- /* must challenge the current owner of the unique name */
- char rdata[6];
- struct in_addr ip, from_ip;
- int search = 0;
-
- 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 = ipgrp;
- }
-
- if (!(d = find_req_subnet(p->ip, bcast)))
- {
- DEBUG(3,("response packet: bcast %s not known\n",
- inet_ntoa(p->ip)));
- return;
- }
-
- if (bcast)
- search &= FIND_LOCAL;
- else
- search &= FIND_WINS;
-
- /* see if the name already exists */
- n = find_name_search(&d, question, search, from_ip);
-
- if (n)
- {
- if (!group) /* unique names */
- {
- if (n->source == SELF || NAME_GROUP(n->nb_flags))
- {
- /* no-one can register one of samba's names, nor can they
- register a name that's a group name as a unique name */
-
- rcode = 6;
- success = False;
- }
- else if(!ip_equal(ip, n->ip))
- {
- /* hm. this unique name doesn't belong to them. */
-
- /* XXXX rfc1001.txt says:
- * if we are doing secured WINS, we must send a Wait-Acknowledge
- * packet (WACK) to the person who wants the name, then do a
- * name query on the person who currently owns the unique name.
- * if the current owner still says they own it, the person who wants
- * the name can't have it. if they do not, or are not alive, they can.
- *
- * if we are doing non-secured WINS (which is much simpler) then
- * we send a message to the person wanting the name saying 'he
- * owns this name: i don't want to hear from you ever again
- * until you've checked with him if you can have it!'. we then
- * abandon the registration. once the person wanting the name
- * has checked with the current owner, they will repeat the
- * registration packet if the current owner is dead or doesn't
- * want the name.
- */
-
- /* non-secured WINS implementation: caller is responsible
- for checking with current owner of name, then getting back
- to us... IF current owner no longer owns the unique name */
-
- /* XXXX please note also that samba cannot cope with
- _receiving_ such redirecting, non-secured registration
- packets. code to do this needs to be added.
- */
-
- rcode = 0;
- success = False;
- recurse = False;
-
- /* we inform on the current owner to the caller (which is
- why it's non-secure */
-
- reply_name = &n->name;
-
- /* name_type = ?;
- name_class = ?;
- XXXX sorry, guys: i really can't see what name_type
- and name_class should be set to according to rfc1001 */
- }
- else
- {
- n->ip = ip;
- n->death_time = ttl?p->timestamp+ttl*3:0;
- DEBUG(3,("%s owner: %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;
- }
- }
-
- /* XXXX bug reported by terryt@ren.pc.athabascau.ca */
- /* names that people have checked for and not found get DNSFAILed.
- we need to update the name record if someone then registers */
-
- if (n->source == DNSFAIL)
- n->source = REGISTER;
-
- }
- else
- {
- /* add the name to our name/subnet, or WINS, database */
- n = add_netbios_entry(d,qname,name_type,nb_flags,ttl,REGISTER,ip,
- True,!bcast);
- }
-
- /* if samba owns a unique name on a subnet, then it must respond and
- disallow the attempted registration. if the registration is
- successful by broadcast, only then is there no need to respond
- (implicit registration: see rfc1001.txt 15.2.1).
- */
-
- if (bcast || !success) return;
-
- rdata[0] = nb_flags;
- rdata[1] = 0;
- putip(&rdata[2],(char *)&ip);
-
- /* Send a NAME REGISTRATION RESPONSE (pos/neg)
- or an END-NODE CHALLENGE REGISTRATION RESPONSE */
- reply_netbios_packet(p,nmb->header.name_trn_id,
- rcode,opcode,recurse,
- reply_name, name_type, name_class,
- ttl,
- rdata, 6);
-}
-
-
-/****************************************************************************
-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;
- char rdata[MAX_DGRAM_SIZE];
- char *countptr, *buf, *bufend;
- int names_added;
- struct name_record *n;
- struct subnet_record *d = NULL;
-
- BOOL bcast = nmb->header.nm_flags.bcast;
-
- if (!(d = find_req_subnet(p->ip, bcast)))
- {
- DEBUG(3,("Name status req: bcast %s not known\n",
- inet_ntoa(p->ip)));
- return;
- }
-
- DEBUG(3,("Name status for name %s %s\n",
- namestr(&nmb->question.question_name), inet_ntoa(p->ip)));
-
- n = find_name_search(&d, &nmb->question.question_name,
- FIND_SELF|FIND_LOCAL,
- p->ip);
-
- if (!n) return;
-
- /* XXXX hack, we should calculate exactly how many will fit */
- bufend = &rdata[MAX_DGRAM_SIZE] - 18;
- countptr = buf = rdata;
- buf += 1;
-
- names_added = 0;
-
- for (n = d->namelist ; n && buf < bufend; 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);
- sprintf(buf,"%-15.15s",n->name.name);
- 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;
- }
-
- /* carry on putting name info in buffer */
-
- buf[15] = name_type;
- buf[16] = n->nb_flags;
-
- buf += 18;
-
- names_added++;
- }
-
- 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,True,
- &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 subnet_record **d,
- struct nmb_name *question,
- struct in_addr ip, int Time, int search)
-{
- 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(d,question,search,ip);
-
- if (*d == NULL) return NULL;
-
- /* 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 */
- DEBUG(3,("no recursion.\n"));
- /* add the fail to our WINS cache of names. give it 1 hour in the cache */
- add_netbios_entry(*d,qname,name_type,NB_ACTIVE,60*60,DNSFAIL,dns_ip,
- True, True);
- return NULL;
- }
-
- /* add it to our WINS cache of names. give it 2 hours in the cache */
- n = add_netbios_entry(*d,qname,name_type,NB_ACTIVE,2*60*60,DNS,dns_ip,
- True,True);
-
- /* 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;
- }
-
- /* 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;
-}
-
-
-/***************************************************************************
-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!
-
-with directed name queries:
-
- - if you are the WINS server, you are expected to respond with either
- a negative response, a positive response, or a wait-for-acknowledgement
- packet, and then later on a pos/neg response.
-
-****************************************************************************/
-void reply_name_query(struct packet_struct *p)
-{
- struct nmb_packet *nmb = &p->packet.nmb;
- struct nmb_name *question = &nmb->question.question_name;
- int name_type = question->name_type;
- 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 subnet_record *d = NULL;
-
- BOOL success = True;
-
- struct name_record *n;
- int search = 0;
-
- if (!(d = find_req_subnet(p->ip, bcast)))
- {
- DEBUG(3,("name query: bcast %s not known\n",
- inet_ntoa(p->ip)));
- success = False;
- }
-
- if (bcast)
- {
- /* a name query has been made by a non-WINS configured host. search the
- local interface database as well */
- search |= FIND_LOCAL;
-
- }
- else if (!special_browser_name(question->name, name_type))
- {
- search |= FIND_WINS;
- }
-
- DEBUG(3,("Name query "));
-
- if (search == 0)
- {
- /* eh? no criterion for searching database. help! */
- success = False;
- }
-
- if (success && (n = search_for_name(&d,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) {
- if (!lp_wins_proxy() || same_net(p->ip,n->ip,*iface_nmask(p->ip))) {
- /* 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, or
- we're replying on behalf of a caller because they are on a different
- subnet and cannot hear the broadcast. XXXX lp_wins_proxy should be
- switched off in environments where broadcasts are forwarded */
-
- /* XXXX note: for proxy servers, we should forward the query on to
- another WINS server if the name is not in our database, or we are
- not a WINS server ourselves
- */
- 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;
- }
-
- /* if the IP is 0 then substitute my IP */
- if (zero_ip(retip)) retip = *iface_ip(p->ip);
-
- if (success)
- {
- rcode = 0;
- DEBUG(3,("OK %s\n",inet_ntoa(retip)));
- }
- else
- {
- rcode = 3;
- DEBUG(3,("UNKNOWN\n"));
- }
-
- if (success)
- {
- rdata[0] = nb_flags;
- rdata[1] = 0;
- putip(&rdata[2],(char *)&retip);
- }
-
- reply_netbios_packet(p,nmb->header.name_trn_id,
- rcode,0,True,
- &nmb->question.question_name,
- nmb->question.question_type,
- nmb->question.question_class,
- ttl,
- rdata, success ? 6 : 0);
-}
-
-
-/****************************************************************************
- response from a name query server check. states of type NAME_QUERY_MST_SRV_CHK,
- NAME_QUERY_SRV_CHK, and NAME_QUERY_FIND_MST dealt with here.
- ****************************************************************************/
-static void response_server_check(struct nmb_name *ans_name,
- struct response_record *n, struct subnet_record *d)
-{
- /* issue another state: this time to do a name status check */
-
- enum state_type cmd = (n->state == NAME_QUERY_MST_SRV_CHK) ?
- NAME_STATUS_MASTER_CHECK : NAME_STATUS_CHECK;
-
- /* initiate a name status check on the server that replied */
- queue_netbios_packet(d,ClientNMB,NMB_STATUS, cmd,
- ans_name->name, ans_name->name_type,
- 0,0,
- False,False,n->to_ip);
-}
-
-/****************************************************************************
- response from a name status check. states of type NAME_STATUS_MASTER_CHECK
- and NAME_STATUS_CHECK dealt with here.
- ****************************************************************************/
-static void response_name_status_check(struct in_addr ip,
- struct nmb_packet *nmb, BOOL bcast,
- struct response_record *n, struct subnet_record *d)
-{
- /* NMB_STATUS arrives: contains workgroup name and server name required.
- amongst other things. */
-
- struct nmb_name name;
- fstring serv_name;
-
- if (interpret_node_status(d,nmb->answers->rdata,
- &name,0x1d,serv_name,ip,bcast))
- {
- if (*serv_name)
- {
- sync_server(n->state,serv_name,
- name.name,name.name_type, n->to_ip);
- }
- }
- else
- {
- DEBUG(1,("No 0x1d name type in interpret_node_status()\n"));
- }
-}
-
-
-/****************************************************************************
- response from a name query to sync browse lists or to update our netbios
- entry. states of type NAME_QUERY_SYNC and NAME_QUERY_CONFIRM
- ****************************************************************************/
-static void response_name_query_sync(struct nmb_packet *nmb,
- struct nmb_name *ans_name, BOOL bcast,
- struct response_record *n, struct subnet_record *d)
-{
- DEBUG(4, ("Name query at %s ip %s - ",
- namestr(&n->name), inet_ntoa(n->to_ip)));
-
- if (!name_equal(&n->name, ans_name))
- {
- /* someone gave us the wrong name as a reply. oops. */
- DEBUG(4,("unexpected name received: %s\n", namestr(ans_name)));
- return;
- }
-
- 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]);
-
- if (!ip_equal(n->to_ip, found_ip))
- {
- /* someone gave us the wrong ip as a reply. oops. */
- DEBUG(4,("expected ip: %s\n", inet_ntoa(n->to_ip)));
- DEBUG(4,("unexpected ip: %s\n", inet_ntoa(found_ip)));
- return;
- }
-
- DEBUG(4, (" OK: %s\n", inet_ntoa(found_ip)));
-
- if (n->state == NAME_QUERY_SYNC)
- {
- struct work_record *work = NULL;
- if ((work = find_workgroupstruct(d, ans_name->name, False)))
- {
- /* the server is there: sync quick before it (possibly) dies! */
- sync_browse_lists(d, work, ans_name->name, ans_name->name_type,
- found_ip);
- }
- }
- else
- {
- /* update our netbios name list (re-register it if necessary) */
- add_netbios_entry(d, ans_name->name, ans_name->name_type,
- nb_flags,GET_TTL(0),REGISTER,
- found_ip,False,!bcast);
- }
- }
- else
- {
- DEBUG(4, (" NEGATIVE RESPONSE!\n"));
-
- if (n->state == NAME_QUERY_CONFIRM)
- {
- /* XXXX remove_netbios_entry()? */
- /* lots of things we ought to do, here. if we get here,
- then we're in a mess: our name database doesn't match
- reality. sort it out
- */
- remove_netbios_name(d,n->name.name, n->name.name_type,
- REGISTER,n->to_ip);
- }
- }
-}
-
-/****************************************************************************
- report the response record type
- ****************************************************************************/
-static void debug_rr_type(int rr_type)
-{
- switch (rr_type)
- {
- case NMB_STATUS: DEBUG(3,("Name status ")); break;
- case NMB_QUERY : DEBUG(3,("Name query ")); break;
- case NMB_REG : DEBUG(3,("Name registration ")); break;
- case NMB_REL : DEBUG(3,("Name release ")); break;
- default : DEBUG(1,("wrong response packet type received")); break;
- }
-}
-
-/****************************************************************************
- report the response record nmbd state
- ****************************************************************************/
-static void debug_state_type(int state)
-{
- /* report the state type to help debugging */
- switch (state)
- {
- case NAME_QUERY_MST_SRV_CHK : DEBUG(4,("MASTER_SVR_CHECK\n")); break;
- case NAME_QUERY_SRV_CHK : DEBUG(4,("NAME_QUERY_SRV_CHK\n")); break;
- case NAME_QUERY_FIND_MST : DEBUG(4,("NAME_QUERY_FIND_MST\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 NAME_QUERY_MST_CHK : DEBUG(4,("NAME_QUERY_MST_CHK\n")); break;
- case NAME_REGISTER : DEBUG(4,("NAME_REGISTER\n")); break;
- case NAME_RELEASE : DEBUG(4,("NAME_RELEASE\n")); break;
- case NAME_QUERY_CONFIRM : DEBUG(4,("NAME_QUERY_CONFIRM\n")); break;
- case NAME_QUERY_SYNC : DEBUG(4,("NAME_QUERY_SYNC\n")); break;
- default: break;
- }
-}
-
-/****************************************************************************
- report any problems with the fact that a response has been received.
-
- (responses for certain types of operations are only expected from one host)
- ****************************************************************************/
-static BOOL response_problem_check(struct response_record *n,
- struct nmb_packet *nmb, char *qname)
-{
- switch (nmb->answers->rr_type)
- {
- case NMB_REL:
- {
- if (n->num_msgs > 1)
- {
- DEBUG(1,("more than one release name response received!\n"));
- return True;
- }
- break;
- }
-
- case NMB_REG:
- {
- if (n->num_msgs > 1)
- {
- DEBUG(1,("more than one register name response received!\n"));
- return True;
- }
- break;
- }
-
- case NMB_QUERY:
- {
- if (n->num_msgs > 1)
- {
- if (nmb->header.rcode == 0 && nmb->answers->rdata)
- {
- int nb_flags = nmb->answers->rdata[0];
-
- if ((!NAME_GROUP(nb_flags)))
- {
- /* oh dear. more than one person responded to a unique name.
- there is either a network problem, a configuration problem
- or a server is mis-behaving */
-
- /* XXXX mark the name as in conflict, and then let the
- person who just responded know that they must also mark it
- as in conflict, and therefore must NOT use it.
- see rfc1001.txt 15.1.3.5 */
-
- /* this may cause problems for some early versions of nmbd */
-
- switch (n->state)
- {
- case NAME_QUERY_MST_SRV_CHK:
- case NAME_QUERY_SRV_CHK:
- case NAME_QUERY_MST_CHK:
- /* don't do case NAME_QUERY_FIND_MST */
- {
- if (!strequal(qname,n->name.name))
- {
- /* one subnet, one master browser per workgroup */
- /* XXXX force an election? */
-
- DEBUG(3,("more than one master browser replied!\n"));
- return True;
- }
- break;
- }
- default: break;
- }
- DEBUG(3,("Unique Name conflict detected!\n"));
- return True;
- }
- }
- else
- {
- /* we have received a negative reply, having already received
- at least one response (pos/neg). something's really wrong! */
-
- DEBUG(3,("wierd name query problem detected!\n"));
- return True;
- }
- }
- }
- }
- return False;
-}
-
-/****************************************************************************
- check that the response received is compatible with the response record
- ****************************************************************************/
-static BOOL response_compatible(struct response_record *n,
- struct nmb_packet *nmb)
-{
- switch (n->state)
- {
- case NAME_RELEASE:
- {
- if (nmb->answers->rr_type != NMB_REL)
- {
- DEBUG(1,("Name release reply has wrong answer rr_type\n"));
- return False;
- }
- break;
- }
-
- case NAME_REGISTER:
- {
- if (nmb->answers->rr_type != NMB_REG)
- {
- DEBUG(1,("Name register reply has wrong answer rr_type\n"));
- return False;
- }
- break;
- }
-
- case NAME_QUERY_CONFIRM:
- case NAME_QUERY_SYNC:
- case NAME_QUERY_MST_SRV_CHK:
- case NAME_QUERY_SRV_CHK:
- case NAME_QUERY_FIND_MST:
- case NAME_QUERY_MST_CHK:
- {
- if (nmb->answers->rr_type != NMB_QUERY)
- {
- DEBUG(1,("Name query reply has wrong answer rr_type\n"));
- return False;
- }
- break;
- }
-
- case NAME_STATUS_MASTER_CHECK:
- case NAME_STATUS_CHECK:
- {
- if (nmb->answers->rr_type != NMB_STATUS)
- {
- DEBUG(1,("Name status reply has wrong answer rr_type\n"));
- return False;
- }
- break;
- }
-
- default:
- {
- DEBUG(1,("unknown state type received in response_netbios_packet\n"));
- return False;
- }
- }
- return True;
-}
-
-
-/****************************************************************************
- process the response packet received
- ****************************************************************************/
-static void response_process(struct subnet_record *d, struct packet_struct *p,
- struct response_record *n, struct nmb_packet *nmb,
- BOOL bcast, struct nmb_name *ans_name)
-{
- switch (n->state)
- {
- case NAME_RELEASE:
- {
- response_name_release(d, p);
- break;
- }
-
- case NAME_REGISTER:
- {
- response_name_reg(d, p);
- break;
- }
-
- case NAME_QUERY_MST_SRV_CHK:
- case NAME_QUERY_SRV_CHK:
- case NAME_QUERY_FIND_MST:
- {
- response_server_check(ans_name, n, d);
- break;
- }
-
- case NAME_STATUS_MASTER_CHECK:
- case NAME_STATUS_CHECK:
- {
- response_name_status_check(p->ip, nmb, bcast, n, d);
- break;
- }
-
- case NAME_QUERY_CONFIRM:
- case NAME_QUERY_SYNC:
- {
- response_name_query_sync(nmb, ans_name, bcast, n, d);
- break;
- }
- case NAME_QUERY_MST_CHK:
- {
- /* 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 (just checking!)\n",
- namestr(&n->name), inet_ntoa(n->to_ip)));
- break;
- }
-
- default:
- {
- DEBUG(1,("unknown state type received in response_netbios_packet\n"));
- break;
- }
- }
-}
-
-
-/****************************************************************************
- response from a netbios packet.
- ****************************************************************************/
-static void response_netbios_packet(struct packet_struct *p)
-{
- struct nmb_packet *nmb = &p->packet.nmb;
- struct nmb_name *question = &nmb->question.question_name;
- struct nmb_name *ans_name = NULL;
- char *qname = question->name;
- BOOL bcast = nmb->header.nm_flags.bcast;
- struct response_record *n;
- struct subnet_record *d = NULL;
-
- if (!(n = find_response_record(&d,nmb->header.name_trn_id))) {
- DEBUG(2,("unknown netbios response (received late or from nmblookup?)\n"));
- return;
- }
-
- if (!d)
- {
- DEBUG(2,("response packet: subnet %s not known\n", inet_ntoa(p->ip)));
- return;
- }
-
- if (!same_net(d->bcast_ip, d->mask_ip, p->ip)) /* copes with WINS 'subnet' */
- {
- DEBUG(2,("response from %s. ", inet_ntoa(p->ip)));
- DEBUG(2,("expected on subnet %s. hmm.\n", inet_ntoa(d->bcast_ip)));
- return;
- }
-
- if (nmb->answers == NULL)
- {
- /* hm. the packet received was a response, but with no answer. wierd! */
- DEBUG(2,("NMB packet response from %s (bcast=%s) - UNKNOWN\n",
- inet_ntoa(p->ip), BOOLSTR(bcast)));
- return;
- }
-
- ans_name = &nmb->answers->rr_name;
- DEBUG(3,("response for %s from %s (bcast=%s)\n",
- namestr(ans_name), inet_ntoa(p->ip), BOOLSTR(bcast)));
-
- debug_rr_type(nmb->answers->rr_type);
-
- n->num_msgs++; /* count number of responses received */
- n->repeat_count = 0; /* don't resend: see expire_netbios_packets() */
-
- debug_state_type(n->state);
-
- /* problem checking: multiple responses etc */
- if (response_problem_check(n, nmb, qname))
- return;
-
- /* now check whether the state has received the correct type of response */
- if (!response_compatible(n, nmb))
- return;
-
- /* now deal with the current state */
- response_process(d, p, n, nmb, bcast, ans_name);
-}
-
-
-/****************************************************************************
- 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 8: /* what is this?? */
- case NMB_REG:
- case NMB_REG_REFRESH:
- {
- if (nmb->header.qdcount==0 || nmb->header.arcount==0) break;
- if (nmb->header.response)
- response_netbios_packet(p); /* response to registration dealt with here */
- 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 NMB_REL:
- {
- if (nmb->header.qdcount==0 || nmb->header.arcount==0)
- {
- DEBUG(2,("netbios release packet rejected\n"));
- break;
- }
-
- if (nmb->header.response)
- response_netbios_packet(p); /* response to reply dealt with in here */
- else
- reply_name_release(p);
- break;
- }
- }
-}
-