diff options
author | Samba Release Account <samba-bugs@samba.org> | 1996-06-29 18:49:20 +0000 |
---|---|---|
committer | Samba Release Account <samba-bugs@samba.org> | 1996-06-29 18:49:20 +0000 |
commit | 7e8c60cfe54060860e5ce20b1c3b8ec6aa5c54da (patch) | |
tree | 8015d1bd6233a588ea491f44673d726d7d32f7f1 /source/nameserv.c | |
parent | 6d81d56f929e763bcf6b1f7a61aabaf884c4aad4 (diff) | |
download | samba-7e8c60cfe54060860e5ce20b1c3b8ec6aa5c54da.tar.gz samba-7e8c60cfe54060860e5ce20b1c3b8ec6aa5c54da.tar.xz samba-7e8c60cfe54060860e5ce20b1c3b8ec6aa5c54da.zip |
luke's first attempt at using cvs
accidentally updated the Makefile
updated the name database structure (again!). this time, there is one
name database per local interface. there is also a pseudo-interface on
ip 255.255.255.255. its purpose is to store WINS name entries. all the
local interface name databases store SELF names only. the WINS name
database stores non-special browser names.
added wins.dat file: records WINS entries in ascii format. this is reloaded
when nmbd restarts.
added repeating code for response packets. timer is in seconds only at the
moment.
updated the response queue code to deal with samba registering with a
WINS server a bit better (added more cases when a response isn't received).
tidied up the response packet processing code and expire_response_queue()
code. added cross references between response received and await-response
expired code.
added over-zealous code that checks all machines that register with samba
as a WINS server (every 10 minutes i think): to see whether they are still
alive or not (see rfc1001.txt)
bug reported by terry@ren.pc.athabascau.ca: DNSFAILed names _stay_ as
DNSFAIL, even though the machine may come back up and REGISTER.
removed update_from_reg() function. it's not necessary, and it does too much.
added code that announces on each local interface samba's ttl as zero and
servertype as zero when nmbd is kill -TERMed
first attempt at putting the first functionality of samba browsing back in
(remote subnets should have samba appear in a workgroup specified through
the lmhosts file)
lots of other miscellaneous tidying up / chopping about.
Diffstat (limited to 'source/nameserv.c')
-rw-r--r-- | source/nameserv.c | 1169 |
1 files changed, 952 insertions, 217 deletions
diff --git a/source/nameserv.c b/source/nameserv.c index 22c1e7dbbaa..7b72f93952a 100644 --- a/source/nameserv.c +++ b/source/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; |