summaryrefslogtreecommitdiff
path: root/source3/nameserv.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/nameserv.c')
-rw-r--r--source3/nameserv.c1169
1 files changed, 952 insertions, 217 deletions
diff --git a/source3/nameserv.c b/source3/nameserv.c
index 22c1e7dbba..7b72f93952 100644
--- a/source3/nameserv.c
+++ b/source3/nameserv.c
@@ -30,6 +30,10 @@
extern int ClientNMB;
extern int ClientDGRAM;
+#define FIND_SELF 0x01
+#define FIND_WINS 0x02
+#define FIND_LOCAL 0x04
+
extern int DEBUGLEVEL;
extern pstring scope;
@@ -38,11 +42,27 @@ extern pstring myname;
extern struct in_addr ipzero;
extern struct in_addr ipgrp;
-/* netbios names database */
-struct name_record *namelist;
+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
@@ -57,19 +77,21 @@ static BOOL name_equal(struct nmb_name *n1,struct nmb_name *n2)
/****************************************************************************
add a netbios name into the namelist
**************************************************************************/
-static void add_name(struct name_record *n)
+static void add_name(struct subnet_record *d, struct name_record *n)
{
struct name_record *n2;
- if (!namelist)
+ if (!d) return;
+
+ if (!d->namelist)
{
- namelist = n;
+ d->namelist = n;
n->prev = NULL;
n->next = NULL;
return;
}
- for (n2 = namelist; n2->next; n2 = n2->next) ;
+ for (n2 = d->namelist; n2->next; n2 = n2->next) ;
n2->next = n;
n->next = NULL;
@@ -80,9 +102,12 @@ static void add_name(struct name_record *n)
remove a name from the namelist. The pointer must be an element just
retrieved
**************************************************************************/
-void remove_name(struct name_record *n)
+void remove_name(struct subnet_record *d, struct name_record *n)
{
- struct name_record *nlist = namelist;
+ struct name_record *nlist;
+ if (!d) return;
+
+ nlist = d->namelist;
while (nlist && nlist != n) nlist = nlist->next;
@@ -96,29 +121,70 @@ void remove_name(struct name_record *n)
/****************************************************************************
- 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.
+ find a name in a namelist
**************************************************************************/
-static struct name_record *find_name_search(struct nmb_name *name,
- enum name_search search,
- struct in_addr ip)
+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 = namelist; ret; ret = ret->next)
+ for (ret = n; ret; ret = ret->next)
{
- if (!name_equal(&ret->name,name)) continue;
-
- if (search == FIND_SELF && ret->source != SELF) continue;
+ if (name_equal(&ret->name,name))
+ {
+ /* self search: self names only */
+ if ((search&FIND_SELF) == FIND_SELF && ret->source != SELF)
+ continue;
+ 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);
+}
/****************************************************************************
@@ -127,48 +193,232 @@ static struct name_record *find_name_search(struct nmb_name *name,
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 (n = namelist; n; n = n->next)
+ for (d = subnetlist; d; d = d->next)
+ for (n = d->namelist; n; n = n->next)
{
- DEBUG(3,("%s %s TTL=%d NBFLAGS=%2x\n",
+ 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 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 - 10 < 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(char *name,int type, enum name_source source,
+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 (type != 0x01 && type != 0x1d && type != 0x1e)
+ search |= FIND_WINS;
make_nmb_name(&nn, name, type, scope);
- n = find_name_search(&nn, FIND_GLOBAL, ip);
+ n = find_name_search(&d, &nn, search, ip);
- if (n && n->source == source) remove_name(n);
+ if (n && n->source == source) remove_name(d,n);
}
/****************************************************************************
- add an entry to the name list
+ 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(char *name, int type, int nb_flags,
- int ttl,
- enum name_source source,
- struct in_addr ip,
- BOOL new_only)
+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 (type == 0x01 || type == 0x1d || type == 0x1e)
+ {
+ /* 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);
@@ -177,7 +427,7 @@ struct name_record *add_netbios_entry(char *name, int type, int nb_flags,
make_nmb_name(&n->name,name,type,scope);
- if ((n2 = find_name_search(&n->name, FIND_GLOBAL, new_only?ipzero:ip)))
+ 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;
@@ -189,7 +439,7 @@ struct name_record *add_netbios_entry(char *name, int type, int nb_flags,
n->nb_flags = nb_flags;
n->source = source;
- if (!n2) add_name(n);
+ 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));
@@ -201,21 +451,39 @@ struct name_record *add_netbios_entry(char *name, int type, int nb_flags,
/****************************************************************************
remove an entry from the name list
****************************************************************************/
-void remove_name_entry(char *name,int type)
+void remove_name_entry(struct subnet_record *d, char *name,int type)
{
if (lp_wins_support())
{
/* we are a WINS server. */
- remove_netbios_name(name,type,SELF,ipzero);
+ /* XXXX assume that if we are a WINS server that we are therefore
+ not pointing to another WINS server as well. this may later NOT
+ actually be true */
+ remove_netbios_name(d,name,type,SELF,ipzero);
}
else
{
- struct in_addr ip;
- ip = ipzero;
+ /* not a WINS server: cannot just remove our own names: we have to
+ ask permission from the WINS server, or if no reply is received,
+ _then_ we can remove the name */
- queue_netbios_pkt_wins(ClientNMB,NMB_REL,NAME_RELEASE,
- name, type, 0,
- False, True, ip);
+ struct name_record n;
+ struct name_record *n2=NULL;
+
+ make_nmb_name(&n.name,name,type,scope);
+
+ if ((n2 = find_name_search(&d, &n.name, FIND_SELF, ipzero)))
+ {
+ /* check name isn't already being de-registered */
+ if (NAME_DEREG(n2->nb_flags))
+ return;
+
+ /* mark the name as in the process of deletion. */
+ n2->nb_flags &= NB_DEREG;
+ }
+ queue_netbios_pkt_wins(d,ClientNMB,NMB_REL,NAME_RELEASE,
+ name, type, 0, 0,
+ False, True, ipzero);
}
}
@@ -223,19 +491,32 @@ void remove_name_entry(char *name,int type)
/****************************************************************************
add an entry to the name list
****************************************************************************/
-void add_name_entry(char *name,int type,int nb_flags)
+void add_my_name_entry(struct subnet_record *d,char *name,int type,int nb_flags)
{
+ BOOL re_reg = False;
+ struct nmb_name n;
+
+ if (!d) return;
+
+ /* not that it particularly matters, but if the SELF name already exists,
+ it must be re-registered, rather than just registered */
+
+ make_nmb_name(&n, name, type, scope);
+ if (find_name(d->namelist, &n, SELF, ipzero))
+ re_reg = True;
+
/* always add our own entries */
- add_netbios_entry(name,type,nb_flags,0,SELF,ipzero,False);
+ add_netbios_entry(d,name,type,nb_flags,0,SELF,ipzero,False,lp_wins_support());
if (!lp_wins_support())
{
- struct in_addr ip;
- ip = ipzero;
+ /* we aren't supporting WINS: register name using broadcast or
+ contact WINS server */
- queue_netbios_pkt_wins(ClientNMB,NMB_REG,NAME_REGISTER,
- name, type, nb_flags,
- False, True, ip);
+ 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);
}
}
@@ -245,38 +526,53 @@ void add_name_entry(char *name,int type,int nb_flags)
**************************************************************************/
void add_my_names(void)
{
- struct in_addr ip;
+ BOOL wins = lp_wins_support();
+ struct subnet_record *d;
- ip = ipzero;
-
- add_name_entry(myname,0x20,NB_ACTIVE);
- add_name_entry(myname,0x03,NB_ACTIVE);
- add_name_entry(myname,0x00,NB_ACTIVE);
- add_name_entry(myname,0x1f,NB_ACTIVE);
+ struct in_addr ip = ipzero;
- add_netbios_entry("*",0x0,NB_ACTIVE,0,SELF,ip,False);
- add_netbios_entry("__SAMBA__",0x20,NB_ACTIVE,0,SELF,ip,False);
- add_netbios_entry("__SAMBA__",0x00,NB_ACTIVE,0,SELF,ip,False);
+ /* each subnet entry, including the WINS one, must have its own
+ netbios name. */
+ /* XXXX if there was a transport layer added to samba (ipx/spx, netbeui
+ etc) then there would be yet _another_ for-loop, this time on the
+ transport type */
+ for (d = subnetlist; d; d = d->next)
+ {
+ add_my_name_entry(d, myname,0x20,NB_ACTIVE);
+ add_my_name_entry(d, myname,0x03,NB_ACTIVE);
+ add_my_name_entry(d, myname,0x00,NB_ACTIVE);
+ add_my_name_entry(d, myname,0x1f,NB_ACTIVE);
- if (lp_wins_support()) {
+ add_netbios_entry(d,"*",0x0,NB_ACTIVE,0,SELF,ip,False,wins);
+ add_netbios_entry(d,"__SAMBA__",0x20,NB_ACTIVE,0,SELF,ip,False,wins);
+ add_netbios_entry(d,"__SAMBA__",0x00,NB_ACTIVE,0,SELF,ip,False,wins);
+
+ if (wins) {
/* the 0x1c name gets added by any WINS server it seems */
- add_name_entry(my_workgroup(),0x1c,NB_ACTIVE|NB_GROUP);
+ add_my_name_entry(d, my_workgroup(),0x1c,NB_ACTIVE|NB_GROUP);
}
}
+}
+
/****************************************************************************
remove all the samba names... from a WINS server if necessary.
**************************************************************************/
void remove_my_names()
{
+ struct subnet_record *d;
+
+ for (d = subnetlist; d; d = d->next)
+ {
struct name_record *n;
- for (n = namelist; n; n = n->next)
+ for (n = d->namelist; n; n = n->next)
{
if (n->source == SELF)
{
/* get all SELF names removed from the WINS server's database */
- remove_name_entry(n->name.name, n->name.name_type);
+ remove_name_entry(d,n->name.name, n->name.name_type);
+ }
}
}
}
@@ -297,15 +593,73 @@ void refresh_my_names(time_t t)
}
/*******************************************************************
+ queries names occasionally. an over-cautious, non-trusting WINS server!
+ ******************************************************************/
+void query_refresh_names(void)
+{
+ struct name_record *n;
+ struct subnet_record *d = find_subnet(ipgrp);
+
+ static time_t lasttime = 0;
+ time_t t = time(NULL);
+
+ 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;
+
+ name_refresh_time = NAME_POLL_INTERVAL * max_count / 2;
+
+ /* if (!lp_poll_wins()) return; polling of registered names allowed */
+
+ if (!d) return;
+
+ if (t - lasttime < NAME_POLL_INTERVAL) return;
+
+ for (n = d->namelist; n; n = n->next)
+ {
+ /* only do unique, registered names */
+
+ if (n->source != REGISTER) continue;
+ if (!NAME_GROUP(n->nb_flags)) continue;
+
+ if (n->refresh_time < t)
+ {
+ 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,
+ False,False,n->ip);
+ count++;
+ }
+
+ if (count >= max_count)
+ {
+ /* don't do too many of these at once, but do enough to
+ cover everyone in the list */
+ return;
+ }
+
+ /* this name will be checked on again, if it's not removed */
+ 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 (n = namelist; n; n = next)
+ for (d = subnetlist; d; d = d->next)
+ {
+ for (n = d->namelist; n; n = next)
{
if (n->death_time && n->death_time < t)
{
@@ -316,7 +670,7 @@ void expire_names(time_t t)
if (n->prev) n->prev->next = n->next;
if (n->next) n->next->prev = n->prev;
- if (namelist == n) namelist = n->next;
+ if (d->namelist == n) d->namelist = n->next;
free(n);
}
@@ -326,12 +680,14 @@ void expire_names(time_t t)
}
}
}
+}
/****************************************************************************
- response for a reg release received
+ response for a reg release received. samba has asked a WINS server if it
+ could release a name.
**************************************************************************/
-void response_name_release(struct packet_struct *p)
+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;
@@ -341,18 +697,24 @@ void response_name_release(struct packet_struct *p)
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(name,type,SELF,found_ip);
+ remove_netbios_name(d,name,type,SELF,found_ip);
}
}
else
{
- DEBUG(1,("name registration for %s rejected!\n",
+ 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. */
}
}
@@ -369,14 +731,29 @@ void reply_name_release(struct packet_struct *p)
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));
- n = find_name_search(&nmb->question.question_name, FIND_GLOBAL, ip);
+ 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)
@@ -384,7 +761,7 @@ void reply_name_release(struct packet_struct *p)
/* success = True;
rcode = 6; */
- remove_name(n);
+ remove_name(d,n);
n = NULL;
}
@@ -408,16 +785,19 @@ void reply_name_release(struct packet_struct *p)
/****************************************************************************
response for a reg request received
**************************************************************************/
-void response_name_reg(struct packet_struct *p)
+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];
struct in_addr found_ip;
int ttl = nmb->answers->ttl;
@@ -427,12 +807,31 @@ void response_name_reg(struct packet_struct *p)
if (ismyip(found_ip)) source = SELF;
- add_netbios_entry(name,type,nb_flags,ttl,source,found_ip,True);
+ add_netbios_entry(d, name,type,nb_flags,ttl,source,found_ip,True,!bcast);
}
else
{
+ struct work_record *work;
+
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. */
+
+ if (!(work = find_workgroupstruct(d, name, False))) return;
+
+ /* remove_netbios_name(d,name,type,SELF,ipzero); */
+
+ if (AM_MASTER(work) && (type == 0x1d || type == 0x1b))
+ {
+ int remove_type = 0;
+ if (type == 0x1d) remove_type = SV_TYPE_MASTER_BROWSER;
+ if (type == 0x1b) remove_type = SV_TYPE_DOMAIN_MASTER;
+
+ become_nonmaster(d, work, remove_type);
+ }
}
}
@@ -458,20 +857,21 @@ void reply_name_reg(struct packet_struct *p)
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 */
+ /* must challenge the current owner of the unique name */
char rdata[6];
-
struct in_addr ip, from_ip;
-
- DEBUG(3,("Name registration for name %s at %s rcode=%d\n",
- namestr(question),inet_ntoa(ip),rcode));
+ 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
@@ -479,8 +879,20 @@ void reply_name_reg(struct packet_struct *p)
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(question, FIND_GLOBAL, from_ip);
+ n = find_name_search(&d, question, search, from_ip);
if (n)
{
@@ -502,10 +914,10 @@ void reply_name_reg(struct packet_struct *p)
* 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 is alive, the person who wants the name
- * can't have it. if they are not alive, they can.
+ * 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-secure WINS (which is much simpler) then
+ * 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
@@ -535,8 +947,6 @@ void reply_name_reg(struct packet_struct *p)
}
else
{
- /* XXXX removed code that checked with the owner of a name */
-
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)));
@@ -550,27 +960,30 @@ void reply_name_reg(struct packet_struct *p)
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 subnet/name database */
- n = add_netbios_entry(qname,name_type,nb_flags,ttl,REGISTER,ip,True);
+ /* 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 (bcast) return;
- if (success)
- {
- update_from_reg(nmb->question.question_name.name,
- nmb->question.question_name.name_type, from_ip);
- }
-
rdata[0] = nb_flags;
rdata[1] = 0;
putip(&rdata[2],(char *)&ip);
/* Send a NAME REGISTRATION RESPONSE (pos/neg)
- or and END-NODE CHALLENGE REGISTRATION RESPONSE */
+ 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,
@@ -591,11 +1004,23 @@ void reply_name_status(struct packet_struct *p)
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(&nmb->question.question_name,FIND_GLOBAL, p->ip);
+ n = find_name_search(&d, &nmb->question.question_name,
+ FIND_SELF|FIND_LOCAL,
+ p->ip);
if (!n) return;
@@ -606,7 +1031,7 @@ void reply_name_status(struct packet_struct *p)
names_added = 0;
- for (n = namelist ; n && buf < bufend; n = n->next)
+ for (n = d->namelist ; n && buf < bufend; n = n->next)
{
int name_type = n->name.name_type;
@@ -666,9 +1091,9 @@ void reply_name_status(struct packet_struct *p)
/***************************************************************************
reply to a name query
****************************************************************************/
-static struct name_record *search_for_name(struct nmb_name *question,
- struct in_addr ip, int Time,
- enum name_search search)
+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;
@@ -678,8 +1103,10 @@ static struct name_record *search_for_name(struct nmb_name *question,
DEBUG(3,("Search for %s from %s - ", namestr(question), inet_ntoa(ip)));
- /* first look up name in cache. use ip as well as name to locate it */
- n = find_name_search(question,search,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)
@@ -702,14 +1129,16 @@ static struct name_record *search_for_name(struct nmb_name *question,
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,True);
+ 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 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,True);
+ /* 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;
@@ -734,42 +1163,112 @@ static struct name_record *search_for_name(struct nmb_name *question,
}
-
/***************************************************************************
- reply to a name query
+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 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 subnet_record *d = NULL;
+
BOOL success = True;
+
struct name_record *n;
- enum name_search search = (dns_type || name_type == 0x1b) ?
- FIND_GLOBAL : FIND_SELF;
+ int search = 0;
+
+ if (name_type == 0x20 || name_type == 0x00 || name_type == 0x1b ||
+ name_type == 0x1f || name_type == 0x03 || name_type == 0x01 ||
+ name_type == 0x1c)
+ {
+ /* search for any of the non-'special browser' names, or for a PDC type
+ (0x1b) name in the WINS database.
+ XXXX should we include name type 0x1c: WINS server type?
+ */
+ search |= FIND_WINS;
+ }
+ else
+ {
+ /* special browser name types e.g
+ ^1^2__MSBROWSE__^2^1, GROUP(1d) and GROUP(1e)
+
+ name_type == 0x01 || name_type == 0x1d || name_type == 0x1e.
+
+ XXXX luke reckons we should be able to search for any SELF name
+ in the WINS database, if we are a primary domain controller.
+ */
+
+ if (!(d = find_req_subnet(p->ip, bcast)))
+ {
+ DEBUG(3,("name query: bcast %s not known\n",
+ inet_ntoa(p->ip)));
+ success = False;
+ }
+
+ /* XXXX delete if shouldn't search for SELF names in WINS database */
+ search |= FIND_WINS;
+ }
+
+ if (bcast)
+ {
+ /* a name query has been made by a non-WINS configured host. search the
+ local interface database as well */
+ search |= FIND_LOCAL;
+ }
DEBUG(3,("Name query "));
- if ((n = search_for_name(question,p->ip,p->timestamp, search)))
+ 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 (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 */
+ /* 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 */
+
ttl = n->death_time - p->timestamp;
retip = n->ip;
nb_flags = n->nb_flags;
@@ -811,169 +1310,349 @@ void reply_name_query(struct packet_struct *p)
}
+/****************************************************************************
+ response from a name query server check. commands 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 command: this time to do a name status check */
+
+ enum cmd_type cmd = (n->cmd_type == 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 query
+ response from a name status check. commands of type NAME_STATUS_MASTER_CHECK
+ and NAME_STATUS_CHECK dealt with here.
****************************************************************************/
-static void response_netbios_packet(struct packet_struct *p)
+static void response_name_status_check(struct in_addr ip,
+ struct nmb_packet *nmb, BOOL bcast,
+ struct response_record *n, struct subnet_record *d)
{
- 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;
+ /* NMB_STATUS arrives: contains workgroup name and server name required.
+ amongst other things. */
- if (nmb->answers == NULL)
+ struct nmb_name name;
+ fstring serv_name;
+
+ if (interpret_node_status(d,nmb->answers->rdata,
+ &name,0x1d,serv_name,ip,bcast))
{
- DEBUG(3,("NMB packet response from %s (bcast=%s) - UNKNOWN\n",
- inet_ntoa(p->ip),
- BOOLSTR(bcast)));
- return;
+ if (*serv_name)
+ {
+ sync_server(n->cmd_type,serv_name,
+ name.name,name.name_type, n->to_ip);
}
-
- if (nmb->answers->rr_type == NMB_STATUS) {
- DEBUG(3,("Name status "));
}
-
- if (nmb->answers->rr_type == NMB_QUERY) {
- DEBUG(3,("Name query "));
+ else
+ {
+ DEBUG(1,("No 0x1d name type in interpret_node_status()\n"));
}
-
- 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)));
+/****************************************************************************
+ response from a name query to sync browse lists or to update our netbios
+ entry. commands 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 (!(n = find_name_query(nmb->header.name_trn_id))) {
- DEBUG(3,("unknown response (received too late or from nmblookup?)\n"));
- 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]);
+
+ DEBUG(4, (" OK: %s\n", inet_ntoa(found_ip)));
+
+ if (n->cmd_type == 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 */
+ add_netbios_entry(d, ans_name->name, ans_name->name_type,
+ nb_flags,GET_TTL(0),STATUS_QUERY,
+ found_ip,False,!bcast);
+ }
+ }
+ else
+ {
+ DEBUG(4, (" NEGATIVE RESPONSE!\n"));
- n->num_msgs++; /* count number of responses received */
+ if (n->cmd_type == 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
+ */
+ }
+ }
+}
- switch (n->cmd_type)
+/****************************************************************************
+ report the response record type
+ ****************************************************************************/
+static void debug_rr_type(int rr_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;
+ 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 command type
+ ****************************************************************************/
+static void debug_cmd_type(int cmd_type)
+{
+ /* report the command type to help debugging */
+ switch (cmd_type)
+ {
+ 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 CHECK_MASTER : DEBUG(4,("CHECK_MASTER\n")); break;
- case NAME_CONFIRM_QUERY : DEBUG(4,("NAME_CONFIRM_QUERY\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;
}
- switch (n->cmd_type)
+}
+
+/****************************************************************************
+ 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)
{
- 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))
+ switch (nmb->answers->rr_type)
{
- /* one subnet, one master browser per workgroup */
- /* XXXX force an election? */
- DEBUG(1,("more than one master browser replied!\n"));
+ case NMB_REL:
+ {
+ if (n->num_msgs > 1)
+ {
+ DEBUG(1,("more than one release name response received!\n"));
+ return True;
}
-
- /* 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);
+ break;
}
- else
+
+ case NMB_REG:
{
- DEBUG(1,("Name query reply has wrong answer rr_type\n"));
+ if (n->num_msgs > 1)
+ {
+ DEBUG(1,("more than one register name response received!\n"));
+ return True;
}
break;
}
- case NAME_STATUS_MASTER_CHECK:
- case NAME_STATUS_CHECK:
+ case NMB_QUERY:
{
- if (nmb->answers->rr_type == NMB_STATUS)
+ if (n->num_msgs > 1)
{
- /* NMB_STATUS arrives: contains the workgroup name
- and server name we require */
- struct nmb_name name;
- fstring serv_name;
+ if (nmb->header.rcode == 0 && nmb->answers->rdata)
+ {
+ int nb_flags = nmb->answers->rdata[0];
- if (interpret_node_status(nmb->answers->rdata,
- &name,0x1d,serv_name,p->ip))
+ if ((!NAME_GROUP(nb_flags)))
{
- if (*serv_name)
+ /* 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->cmd_type)
{
- sync_server(n->cmd_type,serv_name,
- name.name,name.name_type,
- n->to_ip);
+ case NAME_QUERY_MST_SRV_CHK:
+ case NAME_QUERY_SRV_CHK:
+ case NAME_QUERY_MST_CHK:
+ /* don't do case NAME_QUERY_FIND_MST: MSBROWSE isn't a unique name. */
+ {
+ 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
{
- DEBUG(1,("No 0x1d name type in interpret_node_status()\n"));
+ /* 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;
}
}
- else
+ }
+ }
+ 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)
{
- DEBUG(1,("Name status reply has wrong answer rr_type\n"));
+ switch (n->cmd_type)
+ {
+ 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 CHECK_MASTER:
+ case NAME_REGISTER:
{
- /* 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)
+ if (nmb->answers->rr_type != NMB_REG)
{
- DEBUG(1,("more than one master browser!\n"));
+ 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_CONFIRM_QUERY:
+
+ case NAME_STATUS_MASTER_CHECK:
+ case NAME_STATUS_CHECK:
{
- 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)
+ if (nmb->answers->rr_type != NMB_STATUS)
{
- int nb_flags = nmb->answers->rdata[0];
- struct in_addr found_ip;
- putip((char*)&found_ip,&nmb->answers->rdata[2]);
+ DEBUG(1,("Name status reply has wrong answer rr_type\n"));
+ return False;
+ }
+ break;
+ }
- 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,False);
+ default:
+ {
+ DEBUG(0,("unknown command received in response_netbios_packet\n"));
+ break;
}
- else
+ }
+ 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->cmd_type)
{
- DEBUG(4, (" NEGATIVE RESPONSE\n"));
+ 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(0,("unknown command received in response_netbios_packet\n"));
@@ -984,6 +1663,62 @@ static void response_netbios_packet(struct packet_struct *p)
/****************************************************************************
+ 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 (!(d = find_req_subnet(p->ip, bcast)))
+ {
+ DEBUG(3,("response packet: bcast %s not known\n", inet_ntoa(p->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)));
+
+ if (!(n = find_response_record(d,nmb->header.name_trn_id))) {
+ DEBUG(2,("unknown netbios response (received late or from nmblookup?)\n"));
+ return;
+ }
+
+ 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_cmd_type(n->cmd_type);
+
+ /* problem checking: multiple responses etc */
+ if (response_problem_check(n, nmb, qname))
+ return;
+
+ /* now check whether the command has received the correct type of response*/
+ if (!response_compatible(n, nmb))
+ return;
+
+ /* now deal with the command */
+ response_process(d, p, n, nmb, bcast, ans_name);
+}
+
+
+/****************************************************************************
process a nmb packet
****************************************************************************/
void process_nmb(struct packet_struct *p)
@@ -994,13 +1729,13 @@ void process_nmb(struct packet_struct *p)
switch (nmb->header.opcode)
{
- case 5:
- case 8:
- case 9:
+ 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_name_reg(p);
+ response_netbios_packet(p); /* response to registration dealt with here */
else
reply_name_reg(p);
break;
@@ -1040,7 +1775,7 @@ void process_nmb(struct packet_struct *p)
break;
}
- case 6:
+ case NMB_REL:
{
if (nmb->header.qdcount==0 || nmb->header.arcount==0)
{
@@ -1049,7 +1784,7 @@ void process_nmb(struct packet_struct *p)
}
if (nmb->header.response)
- response_name_release(p);
+ response_netbios_packet(p); /* response to reply dealt with in here */
else
reply_name_release(p);
break;