From d78b319062144d14a54408fce77ccc313ad58ee3 Mon Sep 17 00:00:00 2001 From: Samba Release Account Date: Sun, 7 Jul 1996 13:29:56 +0000 Subject: added the recently shuffled and updated source files missed in the previous commit (see previous log message for details) fixed bug in nameservreply.c: wrong macro in use (RSSVAL not IVAL!). did another make proto lkcl --- source/include/proto.h | 120 +++++--- source/namedb.c | 4 +- source/namedbname.c | 547 ++++++++++++++++++++++++++++++++++++ source/namedbresp.c | 151 ++++++++++ source/namedbserver.c | 202 ++++++++++++++ source/namedbsubnet.c | 340 +++++++++++++++++++++++ source/namedbwork.c | 251 +++++++++++++++++ source/namepacket.c | 542 ++++++++++++++++++++++++++++++++++++ source/nameservreply.c | 556 +++++++++++++++++++++++++++++++++++++ source/nameservresp.c | 741 +++++++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 3414 insertions(+), 40 deletions(-) create mode 100644 source/namedbname.c create mode 100644 source/namedbresp.c create mode 100644 source/namedbserver.c create mode 100644 source/namedbsubnet.c create mode 100644 source/namedbwork.c create mode 100644 source/namepacket.c create mode 100644 source/nameservreply.c create mode 100644 source/nameservresp.c (limited to 'source') diff --git a/source/include/proto.h b/source/include/proto.h index 6f92de8586a..437a7e25b9a 100644 --- a/source/include/proto.h +++ b/source/include/proto.h @@ -242,6 +242,7 @@ void sync_server(enum state_type state, char *serv_name, char *work_name, int name_type, struct in_addr ip); void announce_backup(void); +void remove_my_servers(void); void announce_server(struct subnet_record *d, struct work_record *work, char *name, char *comment, time_t ttl, int server_type); void announce_host(void); @@ -250,6 +251,7 @@ void expire_browse_cache(time_t t); struct browse_cache_record *add_browser_entry(char *name, int type, char *wg, time_t ttl, struct in_addr ip); void do_browser_lists(void); +void remove_old_servers(struct work_record *work, time_t t); struct work_record *remove_workgroup(struct subnet_record *d, struct work_record *work); struct work_record *find_workgroupstruct(struct subnet_record *d, @@ -269,6 +271,59 @@ struct server_record *add_server_entry(struct subnet_record *d, void add_my_subnets(char *group); void write_browse_list(void); void expire_servers(time_t t); +BOOL name_equal(struct nmb_name *n1,struct nmb_name *n2); +BOOL ms_browser_name(char *name, int type); +void remove_name(struct subnet_record *d, struct name_record *n); +struct name_record *find_name(struct name_record *n, + struct nmb_name *name, + int search, struct in_addr ip); +struct name_record *find_name_search(struct subnet_record **d, + struct nmb_name *name, + int search, struct in_addr ip); +void dump_names(void); +void load_netbios_names(void); +void remove_netbios_name(struct subnet_record *d, + char *name,int type, enum name_source source, + struct in_addr ip); +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); +void expire_names(time_t t); +struct name_record *search_for_name(struct subnet_record **d, + struct nmb_name *question, + struct in_addr ip, int Time, int search); +void add_response_record(struct subnet_record *d, + struct response_record *n); +void remove_response_record(struct subnet_record *d, + struct response_record *n); +struct response_record *make_response_queue_record(enum state_type state, + int id,int fd, + int quest_type, char *name,int type, int nb_flags, time_t ttl, + BOOL bcast,BOOL recurse, + struct in_addr send_ip, struct in_addr reply_to_ip); +struct response_record *find_response_record(struct subnet_record **d, + uint16 id); +void remove_old_servers(struct work_record *work, time_t t); +struct server_record *add_server_entry(struct subnet_record *d, + struct work_record *work, + char *name,int servertype, + int ttl,char *comment, + BOOL replace); +void expire_servers(time_t t); +struct subnet_record *find_subnet(struct in_addr bcast_ip); +struct subnet_record *find_req_subnet(struct in_addr ip, BOOL bcast); +void add_subnet_interfaces(void); +void add_my_subnets(char *group); +struct subnet_record *add_subnet_entry(struct in_addr bcast_ip, + struct in_addr mask_ip, + char *name, BOOL add, BOOL lmhosts); +void write_browse_list(void); +struct work_record *remove_workgroup(struct subnet_record *d, + struct work_record *work); +struct work_record *find_workgroupstruct(struct subnet_record *d, + fstring name, BOOL add); +void dump_workgroups(void); void check_master_browser(void); void browser_gone(char *work_name, struct in_addr ip); void send_election(struct subnet_record *d, char *group,uint32 criterion, @@ -283,6 +338,20 @@ void run_elections(void); void process_election(struct packet_struct *p,char *buf); BOOL check_elections(void); void process_logon_packet(struct packet_struct *p,char *buf,int len); +void initiate_netbios_packet(uint16 *id, + int fd,int quest_type,char *name,int name_type, + int nb_flags,BOOL bcast,BOOL recurse, + struct in_addr to_ip); +void reply_netbios_packet(struct packet_struct *p1,int trn_id, + int rcode,int opcode, BOOL recurse, + struct nmb_name *rr_name,int rr_type,int rr_class,int ttl, + char *data,int len); +void queue_packet(struct packet_struct *packet); +void run_packet_queue(); +void listen_for_packets(BOOL run_election); +BOOL send_mailslot_reply(char *mailslot,int fd,char *buf,int len,char *srcname, + char *dstname,int src_type,int dest_type, + struct in_addr dest_ip,struct in_addr src_ip); BOOL name_status(int fd,char *name,int name_type,BOOL recurse, struct in_addr to_ip,char *master,char *rname, void (*fn)()); @@ -290,64 +359,39 @@ BOOL name_query(int fd,char *name,int name_type, BOOL bcast,BOOL recurse, struct in_addr to_ip, struct in_addr *ip,void (*fn)()); void expire_netbios_response_entries(); -void reply_netbios_packet(struct packet_struct *p1,int trn_id, - int rcode,int opcode, BOOL recurse, - struct nmb_name *rr_name,int rr_type,int rr_class,int ttl, - char *data,int len); -void queue_netbios_pkt_wins(struct subnet_record *d, +struct response_record *queue_netbios_pkt_wins(struct subnet_record *d, int fd,int quest_type,enum state_type state, char *name,int name_type,int nb_flags, time_t ttl, - BOOL bcast,BOOL recurse,struct in_addr to_ip); -void queue_netbios_packet(struct subnet_record *d, + BOOL bcast,BOOL recurse, + struct in_addr send_ip, struct in_addr reply_to_ip); +struct response_record *queue_netbios_packet(struct subnet_record *d, int fd,int quest_type,enum state_type state,char *name, int name_type,int nb_flags, time_t ttl, - BOOL bcast,BOOL recurse, struct in_addr to_ip); -struct response_record *find_response_record(struct subnet_record **d, - uint16 id); -void queue_packet(struct packet_struct *packet); -void run_packet_queue(); -void listen_for_packets(BOOL run_election); -BOOL interpret_node_status(struct subnet_record *d, - char *p, struct nmb_name *name,int t, - char *serv_name, struct in_addr ip, BOOL bcast); -BOOL send_mailslot_reply(char *mailslot,int fd,char *buf,int len,char *srcname, - char *dstname,int src_type,int dest_type, - struct in_addr dest_ip,struct in_addr src_ip); -BOOL special_browser_name(char *name, int type); -void remove_name(struct subnet_record *d, struct name_record *n); -void dump_names(void); -void load_netbios_names(void); -void remove_netbios_name(struct subnet_record *d, - char *name,int type, enum name_source source, - struct in_addr ip); -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); + BOOL bcast,BOOL recurse, + struct in_addr send_ip, struct in_addr reply_to_ip); void remove_name_entry(struct subnet_record *d, char *name,int type); void add_my_name_entry(struct subnet_record *d,char *name,int type,int nb_flags); void add_my_names(void); void remove_my_names(); void refresh_my_names(time_t t); void query_refresh_names(void); -void expire_names(time_t t); -void response_name_release(struct subnet_record *d, struct packet_struct *p); void reply_name_release(struct packet_struct *p); -void response_name_reg(struct subnet_record *d, struct packet_struct *p); +void send_name_response(int fd, + int name_trn_id, int opcode, BOOL success, BOOL recurse, + struct nmb_name *reply_name, int nb_flags, int ttl, + struct in_addr ip); void reply_name_reg(struct packet_struct *p); void reply_name_status(struct packet_struct *p); -struct name_record *search_for_name(struct subnet_record **d, - struct nmb_name *question, - struct in_addr ip, int Time, int search); void reply_name_query(struct packet_struct *p); -void process_nmb(struct packet_struct *p); +void response_name_release(struct subnet_record *d, struct packet_struct *p); +void response_name_reg(struct subnet_record *d, struct packet_struct *p); +void response_netbios_packet(struct packet_struct *p); void reset_server(char *name, int state, struct in_addr ip); void tell_become_backup(void); BOOL same_context(struct dgram_packet *dgram); BOOL listening_name(struct work_record *work, struct nmb_name *n); BOOL listening_type(struct packet_struct *p, int command); void process_browse_packet(struct packet_struct *p,char *buf,int len); -void process_dgram(struct packet_struct *p); BOOL reload_services(BOOL test); void debug_nmb_packet(struct packet_struct *p); char *namestr(struct nmb_name *n); diff --git a/source/namedb.c b/source/namedb.c index 305dfb44766..96f64393c79 100644 --- a/source/namedb.c +++ b/source/namedb.c @@ -148,7 +148,7 @@ static struct work_record *make_workgroup(char *name) expire old servers in the serverlist time of -1 indicates everybody dies ******************************************************************/ -static void remove_old_servers(struct work_record *work, time_t t) +void remove_old_servers(struct work_record *work, time_t t) { struct server_record *s; struct server_record *nexts; @@ -270,7 +270,7 @@ struct work_record *find_workgroupstruct(struct subnet_record *d, inet_ntoa(d->bcast_ip))); queue_netbios_pkt_wins(d,ClientNMB,NMB_QUERY, NAME_QUERY_FIND_MST, MSBROWSE,0x1,0,0, - True,False, d->bcast_ip); + True,False, d->bcast_ip, d->bcast_ip); return NULL; } diff --git a/source/namedbname.c b/source/namedbname.c new file mode 100644 index 00000000000..3bff6dae7a1 --- /dev/null +++ b/source/namedbname.c @@ -0,0 +1,547 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1996 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Module name: namedbname.c + + Revision History: + + 14 jan 96: lkcl@pires.co.uk + added multiple workgroup domain master support + + 04 jul 96: lkcl@pires.co.uk + created module namedbname containing name database functions +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +extern pstring scope; +extern struct in_addr ipzero; +extern struct in_addr ipgrp; + +extern struct subnet_record *subnetlist; + +#define WINS_LIST "wins.dat" + + +/**************************************************************************** + true if two netbios names are equal +****************************************************************************/ +BOOL name_equal(struct nmb_name *n1,struct nmb_name *n2) +{ + return n1->name_type == n2->name_type && + strequal(n1->name ,n2->name ) && + strequal(n1->scope,n2->scope); +} + + +/**************************************************************************** + true if the netbios name is ^1^2__MSBROWSE__^2^1 + + note: this name is registered if as a master browser or backup browser + you are responsible for a workgroup (when you announce a domain by + broadcasting on your local subnet, you announce it as coming from this + name: see announce_host()). + + **************************************************************************/ +BOOL ms_browser_name(char *name, int type) +{ + return strequal(name,MSBROWSE) && type == 0x01; +} + + +/**************************************************************************** + add a netbios name into the namelist + **************************************************************************/ +static void add_name(struct subnet_record *d, struct name_record *n) +{ + struct name_record *n2; + + if (!d) return; + + if (!d->namelist) + { + d->namelist = n; + n->prev = NULL; + n->next = NULL; + return; + } + + for (n2 = d->namelist; n2->next; n2 = n2->next) ; + + n2->next = n; + n->next = NULL; + n->prev = n2; +} + + +/**************************************************************************** + remove a name from the namelist. The pointer must be an element just + retrieved + **************************************************************************/ +void remove_name(struct subnet_record *d, struct name_record *n) +{ + struct name_record *nlist; + if (!d) return; + + nlist = d->namelist; + + while (nlist && nlist != n) nlist = nlist->next; + + if (nlist) + { + if (nlist->next) nlist->next->prev = nlist->prev; + if (nlist->prev) nlist->prev->next = nlist->next; + free(nlist); + } +} + + +/**************************************************************************** + find a name in a namelist. + **************************************************************************/ +struct name_record *find_name(struct name_record *n, + struct nmb_name *name, + int search, struct in_addr ip) +{ + struct name_record *ret; + + for (ret = n; ret; ret = ret->next) + { + if (name_equal(&ret->name,name)) + { + /* self search: self names only */ + if ((search&FIND_SELF) == FIND_SELF && ret->source != SELF) + continue; + + /* zero ip is either samba's ip or a way of finding a + name without needing to know the ip address */ + if (zero_ip(ip) || ip_equal(ip, ret->ip)) + { + return ret; + } + } + } + return NULL; +} + + +/**************************************************************************** + find a name in the domain database namelist + search can be any of: + FIND_SELF - look exclusively for names the samba server has added for itself + FIND_LOCAL - look for names in the local subnet record. + FIND_WINS - look for names in the WINS record + **************************************************************************/ +struct name_record *find_name_search(struct subnet_record **d, + struct nmb_name *name, + int search, struct in_addr ip) +{ + if (d == NULL) return NULL; /* bad error! */ + + if ((search & FIND_LOCAL) == FIND_LOCAL) + { + if (*d != NULL) + { + return find_name((*d)->namelist, name, search, ip); + } + else + { + DEBUG(4,("local find_name_search with a NULL subnet pointer\n")); + return NULL; + } + } + + if ((search & FIND_WINS) != FIND_WINS) return NULL; + + if (*d == NULL) + { + /* find WINS subnet record */ + *d = find_subnet(ipgrp); + } + + if (*d == NULL) return NULL; + + return find_name((*d)->namelist, name, search, ip); +} + + +/**************************************************************************** + dump a copy of the name table + **************************************************************************/ +void dump_names(void) +{ + struct name_record *n; + struct subnet_record *d; + fstring fname, fnamenew; + time_t t = time(NULL); + + FILE *f; + + strcpy(fname,lp_lockdir()); + trim_string(fname,NULL,"/"); + strcat(fname,"/"); + strcat(fname,WINS_LIST); + strcpy(fnamenew,fname); + strcat(fnamenew,"."); + + f = fopen(fnamenew,"w"); + + if (!f) + { + DEBUG(4,("Can't open %s - %s\n",fnamenew,strerror(errno))); + } + + DEBUG(3,("Dump of local name table:\n")); + + for (d = subnetlist; d; d = d->next) + for (n = d->namelist; n; n = n->next) + { + if (f && ip_equal(d->bcast_ip, ipgrp) && n->source == REGISTER) + { + fstring data; + + /* XXXX i have little imagination as to how to output nb_flags as + anything other than as a hexadecimal number :-) */ + + sprintf(data, "%s#%02x %s %ld %2x", + n->name.name,n->name.name_type, /* XXXX ignore the scope for now */ + inet_ntoa(n->ip), + n->death_time, + n->nb_flags); + fprintf(f, "%s\n", data); + } + + DEBUG(3,("%15s ", inet_ntoa(d->bcast_ip))); + DEBUG(3,("%15s ", inet_ntoa(d->mask_ip))); + DEBUG(3,("%s %15s TTL=%15d NBFLAGS=%2x\n", + namestr(&n->name), + inet_ntoa(n->ip), + n->death_time?n->death_time-t:0, + n->nb_flags)); + } + + fclose(f); + unlink(fname); + chmod(fnamenew,0644); + rename(fnamenew,fname); + + DEBUG(3,("Wrote wins database %s\n",fname)); +} + + +/**************************************************************************** +load a netbios name database file +****************************************************************************/ +void load_netbios_names(void) +{ + struct subnet_record *d = find_subnet(ipgrp); + fstring fname; + + FILE *f; + pstring line; + + if (!d) return; + + strcpy(fname,lp_lockdir()); + trim_string(fname,NULL,"/"); + strcat(fname,"/"); + strcat(fname,WINS_LIST); + + f = fopen(fname,"r"); + + if (!f) { + DEBUG(2,("Can't open wins database file %s\n",fname)); + return; + } + + while (!feof(f)) + { + pstring name_str, ip_str, ttd_str, nb_flags_str; + + pstring name; + int type = 0; + int nb_flags; + time_t ttd; + struct in_addr ipaddr; + + enum name_source source; + + char *ptr; + int count = 0; + + char *p; + + if (!fgets_slash(line,sizeof(pstring),f)) continue; + + if (*line == '#') continue; + + ptr = line; + + if (next_token(&ptr,name_str ,NULL)) ++count; + if (next_token(&ptr,ip_str ,NULL)) ++count; + if (next_token(&ptr,ttd_str ,NULL)) ++count; + if (next_token(&ptr,nb_flags_str,NULL)) ++count; + + if (count <= 0) continue; + + if (count != 4) { + DEBUG(0,("Ill formed wins line")); + DEBUG(0,("[%s]: name#type ip nb_flags abs_time\n",line)); + continue; + } + + /* netbios name. # divides the name from the type (hex): netbios#xx */ + strcpy(name,name_str); + + p = strchr(name,'#'); + + if (p) { + *p = 0; + sscanf(p+1,"%x",&type); + } + + /* decode the netbios flags (hex) and the time-to-die (seconds) */ + sscanf(nb_flags_str,"%x",&nb_flags); + sscanf(ttd_str,"%ld",&ttd); + + ipaddr = *interpret_addr2(ip_str); + + if (ip_equal(ipaddr,ipzero)) { + source = SELF; + } + else + { + source = REGISTER; + } + + DEBUG(4, ("add WINS line: %s#%02x %s %ld %2x\n", + name,type, inet_ntoa(ipaddr), ttd, nb_flags)); + + /* add all entries that have 60 seconds or more to live */ + if (ttd - 60 < time(NULL) || ttd == 0) + { + time_t t = (ttd?ttd-time(NULL):0) / 3; + + /* add netbios entry read from the wins.dat file. IF it's ok */ + add_netbios_entry(d,name,type,nb_flags,t,source,ipaddr,True,True); + } + } + + fclose(f); +} + + +/**************************************************************************** + remove an entry from the name list + ****************************************************************************/ +void remove_netbios_name(struct subnet_record *d, + char *name,int type, enum name_source source, + struct in_addr ip) +{ + struct nmb_name nn; + struct name_record *n; + + make_nmb_name(&nn, name, type, scope); + n = find_name_search(&d, &nn, FIND_LOCAL, ip); + + if (n && n->source == source) remove_name(d,n); +} + + +/**************************************************************************** + add an entry to the name list. + + this is a multi-purpose function. + + it adds samba's own names in to its records on each interface, keeping a + record of whether it is a master browser, domain master, or WINS server. + + it also keeps a record of WINS entries. + + ****************************************************************************/ +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 && type != 0x1b) + { + /* the only broadcast (non-WINS) names we are adding are ours + (SELF) and PDC type names */ + return NULL; + } + } + + n = (struct name_record *)malloc(sizeof(*n)); + if (!n) return(NULL); + + bzero((char *)n,sizeof(*n)); + + make_nmb_name(&n->name,name,type,scope); + + if ((n2 = find_name_search(&d, &n->name, search, new_only?ipzero:ip))) + { + free(n); + if (new_only || (n2->source==SELF && source!=SELF)) return n2; + n = n2; + } + + if (ttl) + n->death_time = time(NULL)+ttl*3; + n->refresh_time = time(NULL)+GET_TTL(ttl); + + n->ip = ip; + n->nb_flags = nb_flags; + n->source = source; + + if (!n2) add_name(d,n); + + DEBUG(3,("Added netbios name %s at %s ttl=%d nb_flags=%2x\n", + namestr(&n->name),inet_ntoa(ip),ttl,nb_flags)); + + return(n); +} + + +/******************************************************************* + expires old names in the namelist + ******************************************************************/ +void expire_names(time_t t) +{ + struct name_record *n; + struct name_record *next; + struct subnet_record *d; + + /* expire old names */ + for (d = subnetlist; d; d = d->next) + { + for (n = d->namelist; n; n = next) + { + if (n->death_time && n->death_time < t) + { + DEBUG(3,("Removing dead name %s\n", namestr(&n->name))); + + next = n->next; + + if (n->prev) n->prev->next = n->next; + if (n->next) n->next->prev = n->prev; + + if (d->namelist == n) d->namelist = n->next; + + free(n); + } + else + { + next = n->next; + } + } + } +} + + +/*************************************************************************** + reply to a name query + ****************************************************************************/ +struct name_record *search_for_name(struct subnet_record **d, + struct nmb_name *question, + struct in_addr ip, int Time, int search) +{ + int name_type = question->name_type; + char *qname = question->name; + BOOL dns_type = name_type == 0x20 || name_type == 0; + + struct name_record *n; + + DEBUG(3,("Search for %s from %s - ", namestr(question), inet_ntoa(ip))); + + /* first look up name in cache */ + n = find_name_search(d,question,search,ip); + + if (*d == NULL) return NULL; + + /* now try DNS lookup. */ + if (!n) + { + struct in_addr dns_ip; + unsigned long a; + + /* only do DNS lookups if the query is for type 0x20 or type 0x0 */ + if (!dns_type) + { + DEBUG(3,("types 0x20 0x1b 0x0 only: name not found\n")); + return NULL; + } + + /* look it up with DNS */ + a = interpret_addr(qname); + + putip((char *)&dns_ip,(char *)&a); + + if (!a) + { + /* no luck with DNS. We could possibly recurse here XXXX */ + DEBUG(3,("no recursion.\n")); + /* add the fail to our WINS cache of names. give it 1 hour in the cache */ + add_netbios_entry(*d,qname,name_type,NB_ACTIVE,60*60,DNSFAIL,dns_ip, + True, True); + return NULL; + } + + /* add it to our WINS cache of names. give it 2 hours in the cache */ + n = add_netbios_entry(*d,qname,name_type,NB_ACTIVE,2*60*60,DNS,dns_ip, + True,True); + + /* failed to add it? yikes! */ + if (!n) return NULL; + } + + /* is our entry already dead? */ + if (n->death_time) + { + if (n->death_time < Time) return False; + } + + /* it may have been an earlier failure */ + if (n->source == DNSFAIL) + { + DEBUG(3,("DNSFAIL\n")); + return NULL; + } + + DEBUG(3,("OK %s\n",inet_ntoa(n->ip))); + + return n; +} + + diff --git a/source/namedbresp.c b/source/namedbresp.c new file mode 100644 index 00000000000..a9abc5d9b54 --- /dev/null +++ b/source/namedbresp.c @@ -0,0 +1,151 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios library routines + Copyright (C) Andrew Tridgell 1994-1996 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Module name: namedbresp.c + +*/ + +#include "includes.h" + +extern int ClientNMB; +extern int ClientDGRAM; + +extern struct subnet_record *subnetlist; + +extern int DEBUGLEVEL; + +extern pstring scope; +extern pstring myname; +extern struct in_addr ipzero; +extern struct in_addr ipgrp; + +int num_response_packets = 0; + +/*************************************************************************** + add an expected response record into the list + **************************************************************************/ +void add_response_record(struct subnet_record *d, + struct response_record *n) +{ + struct response_record *n2; + + if (!d) return; + + num_response_packets++; /* count of total number of packets still around */ + + if (!d->responselist) + { + d->responselist = n; + n->prev = NULL; + n->next = NULL; + return; + } + + for (n2 = d->responselist; n2->next; n2 = n2->next) ; + + n2->next = n; + n->next = NULL; + n->prev = n2; +} + + +/*************************************************************************** + remove an expected response record from the list + **************************************************************************/ +void remove_response_record(struct subnet_record *d, + struct response_record *n) +{ + if (!d) return; + + if (n->prev) n->prev->next = n->next; + if (n->next) n->next->prev = n->prev; + + if (d->responselist == n) d->responselist = n->next; + + free(n); + + num_response_packets--; /* count of total number of packets still around */ +} + + +/**************************************************************************** + create a name query response record + **************************************************************************/ +struct response_record *make_response_queue_record(enum state_type state, + int id,int fd, + int quest_type, char *name,int type, int nb_flags, time_t ttl, + BOOL bcast,BOOL recurse, + struct in_addr send_ip, struct in_addr reply_to_ip) +{ + struct response_record *n; + + if (!name || !name[0]) return NULL; + + if (!(n = (struct response_record *)malloc(sizeof(*n)))) + return(NULL); + + n->response_id = id; + n->state = state; + n->fd = fd; + n->quest_type = quest_type; + make_nmb_name(&n->name, name, type, scope); + n->nb_flags = nb_flags; + n->ttl = ttl; + n->bcast = bcast; + n->recurse = recurse; + n->send_ip = send_ip; + n->reply_to_ip = reply_to_ip; + + n->repeat_interval = 1; /* XXXX should be in ms */ + n->repeat_count = 4; + n->repeat_time = time(NULL) + n->repeat_interval; + + n->num_msgs = 0; + + return n; +} + + +/**************************************************************************** + find a response in a subnet's name query response list. + **************************************************************************/ +struct response_record *find_response_record(struct subnet_record **d, + uint16 id) +{ + struct response_record *n; + + if (!d) return NULL; + + for ((*d) = subnetlist; (*d); (*d) = (*d)->next) + { + for (n = (*d)->responselist; n; n = n->next) + { + if (n->response_id == id) { + return n; + } + } + } + + *d = NULL; + + return NULL; +} + + diff --git a/source/namedbserver.c b/source/namedbserver.c new file mode 100644 index 00000000000..74c7a96baa9 --- /dev/null +++ b/source/namedbserver.c @@ -0,0 +1,202 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1996 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Revision History: + + 14 jan 96: lkcl@pires.co.uk + added multiple workgroup domain master support + + 04 jul 96: lkcl@pires.co.uk + created module namedbserver containing server database functions + +*/ + +#include "includes.h" +#include "smb.h" + +extern int ClientNMB; +extern int ClientDGRAM; + +extern int DEBUGLEVEL; + +extern pstring myname; + +/* this is our domain/workgroup/server database */ +extern struct subnet_record *subnetlist; + +extern BOOL updatedlists; + + +/******************************************************************* + expire old servers in the serverlist + time of -1 indicates everybody dies + ******************************************************************/ +void remove_old_servers(struct work_record *work, time_t t) +{ + struct server_record *s; + struct server_record *nexts; + + /* expire old entries in the serverlist */ + for (s = work->serverlist; s; s = nexts) + { + if (t == -1 || (s->death_time && s->death_time < t)) + { + DEBUG(3,("Removing dead server %s\n",s->serv.name)); + updatedlists = True; + nexts = s->next; + + if (s->prev) s->prev->next = s->next; + if (s->next) s->next->prev = s->prev; + + if (work->serverlist == s) + work->serverlist = s->next; + + free(s); + } + else + { + nexts = s->next; + } + } +} + + +/*************************************************************************** + add a server into the list + **************************************************************************/ +static void add_server(struct work_record *work,struct server_record *s) +{ + struct server_record *s2; + + if (!work->serverlist) { + work->serverlist = s; + s->prev = NULL; + s->next = NULL; + return; + } + + for (s2 = work->serverlist; s2->next; s2 = s2->next) ; + + s2->next = s; + s->next = NULL; + s->prev = s2; +} + + +/**************************************************************************** + add a server entry + ****************************************************************************/ +struct server_record *add_server_entry(struct subnet_record *d, + struct work_record *work, + char *name,int servertype, + int ttl,char *comment, + BOOL replace) +{ + BOOL newentry=False; + struct server_record *s; + + if (name[0] == '*') + { + return (NULL); + } + + for (s = work->serverlist; s; s = s->next) + { + if (strequal(name,s->serv.name)) break; + } + + if (s && !replace) + { + DEBUG(4,("Not replacing %s\n",name)); + return(s); + } + + if (!s || s->serv.type != servertype || !strequal(s->serv.comment, comment)) + updatedlists=True; + + if (!s) + { + newentry = True; + s = (struct server_record *)malloc(sizeof(*s)); + + if (!s) return(NULL); + + bzero((char *)s,sizeof(*s)); + } + + + if (d->my_interface && strequal(lp_workgroup(),work->work_group)) + { + if (servertype) + servertype |= SV_TYPE_LOCAL_LIST_ONLY; + } + else + { + servertype &= ~SV_TYPE_LOCAL_LIST_ONLY; + } + + /* update the entry */ + StrnCpy(s->serv.name,name,sizeof(s->serv.name)-1); + StrnCpy(s->serv.comment,comment,sizeof(s->serv.comment)-1); + strupper(s->serv.name); + s->serv.type = servertype; + s->death_time = servertype ? (ttl?time(NULL)+ttl*3:0) : (time(NULL)-1); + + /* for a domain entry, the comment field refers to the server name */ + + if (s->serv.type & SV_TYPE_DOMAIN_ENUM) strupper(s->serv.comment); + + if (newentry) + { + add_server(work, s); + + DEBUG(3,("Added ")); + } + else + { + DEBUG(3,("Updated ")); + } + + DEBUG(3,("server entry %s of type %x (%s) to %s %s\n", + name,servertype,comment, + work->work_group,inet_ntoa(d->bcast_ip))); + + return(s); +} + + +/******************************************************************* + expire old servers in the serverlist + ******************************************************************/ +void expire_servers(time_t t) +{ + struct subnet_record *d; + + for (d = subnetlist ; d ; d = d->next) + { + struct work_record *work; + + for (work = d->workgrouplist; work; work = work->next) + { + remove_old_servers(work, t); + } + } +} + diff --git a/source/namedbsubnet.c b/source/namedbsubnet.c new file mode 100644 index 00000000000..5ede7b0b814 --- /dev/null +++ b/source/namedbsubnet.c @@ -0,0 +1,340 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1996 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Revision History: + + 14 jan 96: lkcl@pires.co.uk + added multiple workgroup domain master support + + 04 jul 96: lkcl@pires.co.uk + created module namedbsubnet containing subnet database functions + +*/ + +#include "includes.h" +#include "smb.h" + +extern int ClientNMB; +extern int ClientDGRAM; + +extern int DEBUGLEVEL; + +extern struct in_addr ipgrp; +extern struct in_addr ipzero; + +extern pstring myname; + +BOOL updatedlists = True; +int updatecount = 0; + +/* local interfaces structure */ +extern struct interface *local_interfaces; + +/* remote interfaces structure */ +extern struct interface *remote_interfaces; + +/* this is our domain/workgroup/server database */ +struct subnet_record *subnetlist = NULL; + + +/**************************************************************************** + add a domain into the list + **************************************************************************/ +static void add_subnet(struct subnet_record *d) +{ + struct subnet_record *d2; + + if (!subnetlist) + { + subnetlist = d; + d->prev = NULL; + d->next = NULL; + return; + } + + for (d2 = subnetlist; d2->next; d2 = d2->next); + + d2->next = d; + d->next = NULL; + d->prev = d2; +} + + +/**************************************************************************** + find a subnet in the subnetlist + **************************************************************************/ +struct subnet_record *find_subnet(struct in_addr bcast_ip) +{ + struct subnet_record *d; + struct in_addr wins_ip = ipgrp; + + /* search through subnet list for broadcast/netmask that matches + the source ip address. a subnet 255.255.255.255 represents the + WINS list. */ + + for (d = subnetlist; d; d = d->next) + { + if (ip_equal(bcast_ip, wins_ip)) + { + if (ip_equal(bcast_ip, d->bcast_ip)) + { + return d; + } + } + else if (same_net(bcast_ip, d->bcast_ip, d->mask_ip)) + { + return(d); + } + } + + return (NULL); +} + + +/**************************************************************************** + 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 + ****************************************************************************/ +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); +} + + +/**************************************************************************** + create a domain entry + ****************************************************************************/ +static struct subnet_record *make_subnet(struct in_addr bcast_ip, struct in_addr mask_ip) +{ + struct subnet_record *d; + d = (struct subnet_record *)malloc(sizeof(*d)); + + if (!d) return(NULL); + + bzero((char *)d,sizeof(*d)); + + DEBUG(4, ("making domain %s ", inet_ntoa(bcast_ip))); + DEBUG(4, ("%s\n", inet_ntoa(mask_ip))); + + d->bcast_ip = bcast_ip; + d->mask_ip = mask_ip; + d->workgrouplist = NULL; + d->my_interface = False; /* True iff the interface is on the samba host */ + + add_subnet(d); + + return d; +} + + +/**************************************************************************** + add the remote interfaces from lp_remote_interfaces() and lp_interfaces() + to the netbios subnet database. + ****************************************************************************/ +void add_subnet_interfaces(void) +{ + struct interface *i; + + /* loop on all local interfaces */ + for (i = local_interfaces; i; i = i->next) + { + /* add the interface into our subnet database */ + if (!find_subnet(i->bcast)) + { + struct subnet_record *d = make_subnet(i->bcast,i->nmask); + if (d) + { + /* short-cut method to identifying local interfaces */ + d->my_interface = True; + } + } + } + + /* loop on all remote interfaces */ + for (i = remote_interfaces; i; i = i->next) + { + /* add the interface into our subnet database */ + if (!find_subnet(i->bcast)) + { + make_subnet(i->bcast,i->nmask); + } + } + + /* add the pseudo-ip interface for WINS: 255.255.255.255 */ + if (lp_wins_support()) + { + struct in_addr wins_bcast = ipgrp; + struct in_addr wins_nmask = ipzero; + make_subnet(wins_bcast, wins_nmask); + } +} + + + +/**************************************************************************** + add the default workgroup into my domain + **************************************************************************/ +void add_my_subnets(char *group) +{ + struct interface *i; + + /* add or find domain on our local subnet, in the default workgroup */ + + if (*group == '*') return; + + /* the coding choice is up to you, andrew: i can see why you don't want + global access to the local_interfaces structure: so it can't get + messed up! */ + for (i = local_interfaces; i; i = i->next) + { + add_subnet_entry(i->bcast,i->nmask,group, True, False); + } +} + + +/**************************************************************************** + add a domain entry. creates a workgroup, if necessary, and adds the domain + to the named a workgroup. + ****************************************************************************/ +struct subnet_record *add_subnet_entry(struct in_addr bcast_ip, + struct in_addr mask_ip, + char *name, BOOL add, BOOL lmhosts) +{ + struct subnet_record *d; + + /* XXXX andrew: struct in_addr ip appears not to be referenced at all except + in the DEBUG comment. i assume that the DEBUG comment below actually + intends to refer to bcast_ip? i don't know. + + struct in_addr ip = ipgrp; + + */ + + if (zero_ip(bcast_ip)) + bcast_ip = *iface_bcast(bcast_ip); + + /* add the domain into our domain database */ + if ((d = find_subnet(bcast_ip)) || + (d = make_subnet(bcast_ip, mask_ip))) + { + struct work_record *w = find_workgroupstruct(d, name, add); + extern pstring ServerComment; + + if (!w) return NULL; + + /* add WORKGROUP(1e) and WORKGROUP(00) entries into name database + or register with WINS server, if it's our workgroup */ + if (strequal(lp_workgroup(), name) && d->my_interface) + { + add_my_name_entry(d,name,0x1e,NB_ACTIVE|NB_GROUP); + add_my_name_entry(d,name,0x0 ,NB_ACTIVE|NB_GROUP); + } + /* add samba server name to workgroup list */ + if ((strequal(lp_workgroup(), name) && d->my_interface) || lmhosts) + { + add_server_entry(d,w,myname,w->ServerType,0,ServerComment,True); + } + + DEBUG(3,("Added domain name entry %s at %s\n", name,inet_ntoa(bcast_ip))); + return d; + } + return NULL; +} + + +/******************************************************************* + write out browse.dat + ******************************************************************/ +void write_browse_list(void) +{ + struct subnet_record *d; + + pstring fname,fnamenew; + FILE *f; + + if (!updatedlists) return; + + dump_names(); + dump_workgroups(); + + updatedlists = False; + updatecount++; + + strcpy(fname,lp_lockdir()); + trim_string(fname,NULL,"/"); + strcat(fname,"/"); + strcat(fname,SERVER_LIST); + strcpy(fnamenew,fname); + strcat(fnamenew,"."); + + f = fopen(fnamenew,"w"); + + if (!f) + { + DEBUG(4,("Can't open %s - %s\n",fnamenew,strerror(errno))); + return; + } + + for (d = subnetlist; d ; d = d->next) + { + struct work_record *work; + for (work = d->workgrouplist; work ; work = work->next) + { + struct server_record *s; + for (s = work->serverlist; s ; s = s->next) + { + fstring tmp; + + /* don't list domains I don't have a master for */ + if ((s->serv.type & SV_TYPE_DOMAIN_ENUM) && !s->serv.comment[0]) + { + continue; + } + + /* output server details, plus what workgroup/domain + they're in. without the domain information, the + combined list of all servers in all workgroups gets + sent to anyone asking about any workgroup! */ + + sprintf(tmp, "\"%s\"", s->serv.name); + fprintf(f, "%-25s ", tmp); + fprintf(f, "%08x ", s->serv.type); + sprintf(tmp, "\"%s\" ", s->serv.comment); + fprintf(f, "%-30s", tmp); + fprintf(f, "\"%s\"\n", work->work_group); + } + } + } + + fclose(f); + unlink(fname); + chmod(fnamenew,0644); + rename(fnamenew,fname); + DEBUG(3,("Wrote browse list %s\n",fname)); +} + + diff --git a/source/namedbwork.c b/source/namedbwork.c new file mode 100644 index 00000000000..7937aa4512c --- /dev/null +++ b/source/namedbwork.c @@ -0,0 +1,251 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1996 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Revision History: + + 14 jan 96: lkcl@pires.co.uk + added multiple workgroup domain master support + + 04 jul 96: lkcl@pires.co.uk + created module namedbwork containing workgroup database functions + +*/ + +#include "includes.h" +#include "smb.h" + +extern int ClientNMB; + +extern int DEBUGLEVEL; + +/* this is our domain/workgroup/server database */ +extern struct subnet_record *subnetlist; + +int workgroup_count = 0; /* unique index key: one for each workgroup */ + +/* what server type are we currently */ + +#define DFLT_SERVER_TYPE (SV_TYPE_WORKSTATION | SV_TYPE_SERVER | \ + SV_TYPE_TIME_SOURCE | SV_TYPE_SERVER_UNIX | \ + SV_TYPE_PRINTQ_SERVER | SV_TYPE_POTENTIAL_BROWSER) + + +/**************************************************************************** + add a workgroup into the domain list + **************************************************************************/ +static void add_workgroup(struct work_record *work, struct subnet_record *d) +{ + struct work_record *w2; + + if (!work || !d) return; + + if (!d->workgrouplist) + { + d->workgrouplist = work; + work->prev = NULL; + work->next = NULL; + return; + } + + for (w2 = d->workgrouplist; w2->next; w2 = w2->next); + + w2->next = work; + work->next = NULL; + work->prev = w2; +} + + +/**************************************************************************** + create a blank workgroup + **************************************************************************/ +static struct work_record *make_workgroup(char *name) +{ + struct work_record *work; + struct subnet_record *d; + int t = -1; + + if (!name || !name[0]) return NULL; + + work = (struct work_record *)malloc(sizeof(*work)); + if (!work) return(NULL); + + StrnCpy(work->work_group,name,sizeof(work->work_group)-1); + work->serverlist = NULL; + + work->ServerType = DFLT_SERVER_TYPE; + work->RunningElection = False; + work->ElectionCount = 0; + work->needelection = False; + work->needannounce = True; + work->state = MST_NONE; + + /* make sure all token representations of workgroups are unique */ + + for (d = subnetlist; d && t == -1; d = d->next) + { + struct work_record *w; + for (w = d->workgrouplist; w && t == -1; w = w->next) + { + if (strequal(w->work_group, work->work_group)) t = w->token; + } + } + + if (t == -1) + { + work->token = ++workgroup_count; + } + else + { + work->token = t; + } + + + /* WfWg uses 01040b01 */ + /* Win95 uses 01041501 */ + /* NTAS uses ???????? */ + work->ElectionCriterion = (MAINTAIN_LIST<<1)|(ELECTION_VERSION<<8); + work->ElectionCriterion |= (lp_os_level() << 24); + if (lp_domain_master()) { + work->ElectionCriterion |= 0x80; + } + + return work; +} + + +/******************************************************************* + remove workgroups + ******************************************************************/ +struct work_record *remove_workgroup(struct subnet_record *d, + struct work_record *work) +{ + struct work_record *ret_work = NULL; + + if (!d || !work) return NULL; + + DEBUG(3,("Removing old workgroup %s\n", work->work_group)); + + remove_old_servers(work, -1); + + ret_work = work->next; + + if (work->prev) work->prev->next = work->next; + if (work->next) work->next->prev = work->prev; + + if (d->workgrouplist == work) d->workgrouplist = work->next; + + free(work); + + return ret_work; +} + + +/**************************************************************************** + find a workgroup in the workgrouplist + only create it if the domain allows it, or the parameter 'add' insists + that it get created/added anyway. this allows us to force entries in + lmhosts file to be added. + **************************************************************************/ +struct work_record *find_workgroupstruct(struct subnet_record *d, + fstring name, BOOL add) +{ + struct work_record *ret, *work; + + if (!d) return NULL; + + DEBUG(4, ("workgroup search for %s: ", name)); + + if (strequal(name, "*")) + { + DEBUG(2,("add any workgroups: initiating browser search on %s\n", + inet_ntoa(d->bcast_ip))); + queue_netbios_pkt_wins(d,ClientNMB,NMB_QUERY, NAME_QUERY_FIND_MST, + MSBROWSE,0x1,0,0, + True,False, d->bcast_ip, d->bcast_ip); + return NULL; + } + + for (ret = d->workgrouplist; ret; ret = ret->next) { + if (!strcmp(ret->work_group,name)) { + DEBUG(4, ("found\n")); + return(ret); + } + } + + if (!add) { + DEBUG(4, ("not found\n")); + return NULL; + } + + DEBUG(4,("not found: creating\n")); + + if ((work = make_workgroup(name))) + { + if (lp_preferred_master() && + strequal(lp_workgroup(), name) && + d->my_interface) + { + DEBUG(3, ("preferred master startup for %s\n", work->work_group)); + work->needelection = True; + work->ElectionCriterion |= (1<<3); + } + if (!d->my_interface) + { + work->needelection = False; + } + add_workgroup(work, d); + return(work); + } + return NULL; +} + + +/**************************************************************************** + dump a copy of the workgroup/domain database + **************************************************************************/ +void dump_workgroups(void) +{ + struct subnet_record *d; + + for (d = subnetlist; d; d = d->next) + { + if (d->workgrouplist) + { + struct work_record *work; + + DEBUG(4,("dump domain bcast=%15s: ", inet_ntoa(d->bcast_ip))); + DEBUG(4,(" netmask=%15s:\n", inet_ntoa(d->mask_ip))); + + for (work = d->workgrouplist; work; work = work->next) + { + DEBUG(4,("\t%s(%d)\n", work->work_group, work->token)); + if (work->serverlist) + { + struct server_record *s; + for (s = work->serverlist; s; s = s->next) + { + DEBUG(4,("\t\t%s %8x (%s)\n", + s->serv.name, s->serv.type, s->serv.comment)); + } + } + } + } + } +} diff --git a/source/namepacket.c b/source/namepacket.c new file mode 100644 index 00000000000..38b8ed06535 --- /dev/null +++ b/source/namepacket.c @@ -0,0 +1,542 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1995 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Revision History: + + 14 jan 96: lkcl@pires.co.uk + added multiple workgroup domain master support + +*/ + +#include "includes.h" + +extern int ClientNMB; +extern int ClientDGRAM; + +extern int DEBUGLEVEL; + +extern int num_response_packets; + +BOOL CanRecurse = True; +extern pstring scope; +extern struct in_addr ipgrp; + +static uint16 name_trn_id=0; + +/*************************************************************************** + updates the unique transaction identifier + **************************************************************************/ +static void update_name_trn_id(void) +{ + if (!name_trn_id) + { + name_trn_id = (time(NULL)%(unsigned)0x7FFF) + (getpid()%(unsigned)100); + } + name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF; +} + + +/**************************************************************************** + initiate a netbios packet + ****************************************************************************/ +void initiate_netbios_packet(uint16 *id, + int fd,int quest_type,char *name,int name_type, + int nb_flags,BOOL bcast,BOOL recurse, + struct in_addr to_ip) +{ + struct packet_struct p; + struct nmb_packet *nmb = &p.packet.nmb; + struct res_rec additional_rec; + char *packet_type = "unknown"; + int opcode = -1; + + if (!id) return; + + if (quest_type == NMB_STATUS) { packet_type = "nmb_status"; opcode = 0; } + if (quest_type == NMB_QUERY ) { packet_type = "nmb_query"; opcode = 0; } + if (quest_type == NMB_REG ) { packet_type = "nmb_reg"; opcode = 5; } + if (quest_type == NMB_REL ) { packet_type = "nmb_rel"; opcode = 6; } + + DEBUG(4,("initiating netbios packet: %s %s(%x) (bcast=%s) %s\n", + packet_type, name, name_type, BOOLSTR(bcast), inet_ntoa(to_ip))); + + if (opcode == -1) return; + + bzero((char *)&p,sizeof(p)); + + update_name_trn_id(); + + if (*id == 0xffff) *id = name_trn_id; /* allow resending with same id */ + + nmb->header.name_trn_id = *id; + nmb->header.opcode = opcode; + nmb->header.response = False; + + nmb->header.nm_flags.bcast = bcast; + nmb->header.nm_flags.recursion_available = CanRecurse; + nmb->header.nm_flags.recursion_desired = recurse; + nmb->header.nm_flags.trunc = False; + nmb->header.nm_flags.authoritative = False; + + nmb->header.rcode = 0; + nmb->header.qdcount = 1; + nmb->header.ancount = 0; + nmb->header.nscount = 0; + nmb->header.arcount = (quest_type==NMB_REG || quest_type==NMB_REL) ? 1 : 0; + + make_nmb_name(&nmb->question.question_name,name,name_type,scope); + + nmb->question.question_type = quest_type; + nmb->question.question_class = 0x1; + + if (quest_type == NMB_REG || quest_type == NMB_REL) + { + nmb->additional = &additional_rec; + bzero((char *)nmb->additional,sizeof(*nmb->additional)); + + nmb->additional->rr_name = nmb->question.question_name; + nmb->additional->rr_type = nmb->question.question_type; + nmb->additional->rr_class = nmb->question.question_class; + + nmb->additional->ttl = quest_type == NMB_REG ? lp_max_ttl() : 0; + nmb->additional->rdlength = 6; + nmb->additional->rdata[0] = nb_flags; + putip(&nmb->additional->rdata[2],(char *)iface_ip(to_ip)); + } + + p.ip = to_ip; + p.port = NMB_PORT; + p.fd = fd; + p.timestamp = time(NULL); + p.packet_type = NMB_PACKET; + + if (!send_packet(&p)) *id = 0xffff; + + return; +} + + +/**************************************************************************** + reply to a netbios name packet + ****************************************************************************/ +void reply_netbios_packet(struct packet_struct *p1,int trn_id, + int rcode,int opcode, BOOL recurse, + struct nmb_name *rr_name,int rr_type,int rr_class,int ttl, + char *data,int len) +{ + struct packet_struct p; + struct nmb_packet *nmb = &p.packet.nmb; + struct res_rec answers; + char *packet_type = "unknown"; + BOOL recursion_desired = False; + + p = *p1; + + switch (rr_type) + { + case NMB_STATUS: + { + packet_type = "nmb_status"; + recursion_desired = True; + break; + } + case NMB_QUERY: + { + packet_type = "nmb_query"; + recursion_desired = True; + break; + } + case NMB_REG: + { + packet_type = "nmb_reg"; + recursion_desired = True; + break; + } + case NMB_REL: + { + packet_type = "nmb_rel"; + recursion_desired = False; + break; + } + case NMB_WAIT_ACK: + { + packet_type = "nmb_wack"; + recursion_desired = False; + break; + } + default: + { + DEBUG(1,("replying netbios packet: %s %s\n", + packet_type, namestr(rr_name), inet_ntoa(p.ip))); + + return; + } + } + + DEBUG(4,("replying netbios packet: %s %s\n", + packet_type, namestr(rr_name), inet_ntoa(p.ip))); + + nmb->header.name_trn_id = trn_id; + nmb->header.opcode = opcode; + nmb->header.response = True; + nmb->header.nm_flags.bcast = False; + nmb->header.nm_flags.recursion_available = recurse; + nmb->header.nm_flags.recursion_desired = recursion_desired; + nmb->header.nm_flags.trunc = False; + nmb->header.nm_flags.authoritative = True; + + nmb->header.qdcount = 0; + nmb->header.ancount = 1; + nmb->header.nscount = 0; + nmb->header.arcount = 0; + nmb->header.rcode = 0; + + bzero((char*)&nmb->question,sizeof(nmb->question)); + + nmb->answers = &answers; + bzero((char*)nmb->answers,sizeof(*nmb->answers)); + + nmb->answers->rr_name = *rr_name; + nmb->answers->rr_type = rr_type; + nmb->answers->rr_class = rr_class; + nmb->answers->ttl = ttl; + + if (data && len) + { + nmb->answers->rdlength = len; + memcpy(nmb->answers->rdata, data, len); + } + + p.packet_type = NMB_PACKET; + + debug_nmb_packet(&p); + + send_packet(&p); +} + + +/******************************************************************* + the global packet linked-list. incoming entries are added to the + end of this list. it is supposed to remain fairly short so we + won't bother with an end pointer. + ******************************************************************/ +static struct packet_struct *packet_queue = NULL; + +/******************************************************************* + queue a packet into the packet queue + ******************************************************************/ +void queue_packet(struct packet_struct *packet) +{ + struct packet_struct *p; + + if (!packet_queue) { + packet->prev = NULL; + packet->next = NULL; + packet_queue = packet; + return; + } + + /* find the bottom */ + for (p=packet_queue;p->next;p=p->next) ; + + p->next = packet; + packet->next = NULL; + packet->prev = p; +} + + +/**************************************************************************** + process udp 138 datagrams + ****************************************************************************/ +static void process_dgram(struct packet_struct *p) +{ + char *buf; + char *buf2; + int len; + struct dgram_packet *dgram = &p->packet.dgram; + + if (dgram->header.msg_type != 0x10 && + dgram->header.msg_type != 0x11 && + dgram->header.msg_type != 0x12) { + /* don't process error packets etc yet */ + return; + } + + buf = &dgram->data[0]; + buf -= 4; /* XXXX for the pseudo tcp length - + someday I need to get rid of this */ + + if (CVAL(buf,smb_com) != SMBtrans) return; + + len = SVAL(buf,smb_vwv11); + buf2 = smb_base(buf) + SVAL(buf,smb_vwv12); + + DEBUG(4,("datagram from %s to %s for %s of type %d len=%d\n", + namestr(&dgram->source_name),namestr(&dgram->dest_name), + smb_buf(buf),CVAL(buf2,0),len)); + + + if (len <= 0) return; + + /* datagram packet received for the browser mailslot */ + if (strequal(smb_buf(buf),BROWSE_MAILSLOT)) { + process_browse_packet(p,buf2,len); + return; + } + + /* datagram packet received for the domain log on mailslot */ + if (strequal(smb_buf(buf),NET_LOGON_MAILSLOT)) { + process_logon_packet(p,buf2,len); + return; + } +} + +/**************************************************************************** + process a nmb packet + ****************************************************************************/ +static void process_nmb(struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + + debug_nmb_packet(p); + + switch (nmb->header.opcode) + { + case 8: /* what is this?? */ + case NMB_REG: + case NMB_REG_REFRESH: + { + if (nmb->header.qdcount==0 || nmb->header.arcount==0) break; + if (nmb->header.response) + response_netbios_packet(p); /* response to registration dealt with here */ + else + reply_name_reg(p); + break; + } + + case 0: + { + if (nmb->header.response) + { + switch (nmb->question.question_type) + { + case 0x0: + { + response_netbios_packet(p); + break; + } + } + return; + } + else if (nmb->header.qdcount>0) + { + switch (nmb->question.question_type) + { + case NMB_QUERY: + { + reply_name_query(p); + break; + } + case NMB_STATUS: + { + reply_name_status(p); + break; + } + } + return; + } + break; + } + + case NMB_REL: + { + if (nmb->header.qdcount==0 || nmb->header.arcount==0) + { + DEBUG(2,("netbios release packet rejected\n")); + break; + } + + if (nmb->header.response) + response_netbios_packet(p); /* response to reply dealt with in here */ + else + reply_name_release(p); + break; + } + } +} + + +/******************************************************************* + run elements off the packet queue till its empty + ******************************************************************/ +void run_packet_queue() +{ + struct packet_struct *p; + + while ((p=packet_queue)) + { + switch (p->packet_type) + { + case NMB_PACKET: + process_nmb(p); + break; + + case DGRAM_PACKET: + process_dgram(p); + break; + } + + packet_queue = packet_queue->next; + if (packet_queue) packet_queue->prev = NULL; + free_packet(p); + } +} + +/**************************************************************************** + listens for NMB or DGRAM packets, and queues them + ***************************************************************************/ +void listen_for_packets(BOOL run_election) +{ + fd_set fds; + int selrtn; + struct timeval timeout; + + FD_ZERO(&fds); + FD_SET(ClientNMB,&fds); + FD_SET(ClientDGRAM,&fds); + + /* during elections and when expecting a netbios response packet we need + to send election packets at one second intervals. + XXXX actually, it needs to be the interval (in ms) between time now and the + time we are expecting the next netbios packet */ + + timeout.tv_sec = (run_election||num_response_packets) ? 1 : NMBD_SELECT_LOOP; + timeout.tv_usec = 0; + + selrtn = sys_select(&fds,&timeout); + + if (FD_ISSET(ClientNMB,&fds)) + { + struct packet_struct *packet = read_packet(ClientNMB, NMB_PACKET); + if (packet) { +#if 1 + if (ismyip(packet->ip) && + (packet->port == NMB_PORT || packet->port == DGRAM_PORT)) { + DEBUG(5,("discarding own packet from %s:%d\n", + inet_ntoa(packet->ip),packet->port)); + free_packet(packet); + } else +#endif + { + queue_packet(packet); + } + } + } + + if (FD_ISSET(ClientDGRAM,&fds)) + { + struct packet_struct *packet = read_packet(ClientDGRAM, DGRAM_PACKET); + if (packet) { +#if 1 + if (ismyip(packet->ip) && + (packet->port == NMB_PORT || packet->port == DGRAM_PORT)) { + DEBUG(5,("discarding own packet from %s:%d\n", + inet_ntoa(packet->ip),packet->port)); + free_packet(packet); + } else +#endif + { + queue_packet(packet); + } + } + } +} + + + +/**************************************************************************** + construct and send a netbios DGRAM + + Note that this currently sends all answers to port 138. thats the + wrong things to do! I should send to the requestors port. XXX + **************************************************************************/ +BOOL send_mailslot_reply(char *mailslot,int fd,char *buf,int len,char *srcname, + char *dstname,int src_type,int dest_type, + struct in_addr dest_ip,struct in_addr src_ip) +{ + struct packet_struct p; + struct dgram_packet *dgram = &p.packet.dgram; + struct in_addr wins_ip = ipgrp; + char *ptr,*p2; + char tmp[4]; + + /* ha ha. no. do NOT send packets to 255.255.255.255: it's a pseudo address */ + if (ip_equal(wins_ip, dest_ip)) return False; + + bzero((char *)&p,sizeof(p)); + + update_name_trn_id(); + + dgram->header.msg_type = 0x11; /* DIRECT GROUP DATAGRAM */ + dgram->header.flags.node_type = M_NODE; + dgram->header.flags.first = True; + dgram->header.flags.more = False; + dgram->header.dgm_id = name_trn_id; + dgram->header.source_ip = src_ip; + dgram->header.source_port = DGRAM_PORT; + dgram->header.dgm_length = 0; /* let build_dgram() handle this */ + dgram->header.packet_offset = 0; + + make_nmb_name(&dgram->source_name,srcname,src_type,scope); + make_nmb_name(&dgram->dest_name,dstname,dest_type,scope); + + ptr = &dgram->data[0]; + + /* now setup the smb part */ + ptr -= 4; /* XXX ugliness because of handling of tcp SMB length */ + memcpy(tmp,ptr,4); + set_message(ptr,17,17 + len,True); + memcpy(ptr,tmp,4); + + CVAL(ptr,smb_com) = SMBtrans; + SSVAL(ptr,smb_vwv1,len); + SSVAL(ptr,smb_vwv11,len); + SSVAL(ptr,smb_vwv12,70 + strlen(mailslot)); + SSVAL(ptr,smb_vwv13,3); + SSVAL(ptr,smb_vwv14,1); + SSVAL(ptr,smb_vwv15,1); + SSVAL(ptr,smb_vwv16,2); + p2 = smb_buf(ptr); + strcpy(p2,mailslot); + p2 = skip_string(p2,1); + + memcpy(p2,buf,len); + p2 += len; + + dgram->datasize = PTR_DIFF(p2,ptr+4); /* +4 for tcp length */ + + p.ip = dest_ip; + p.port = DGRAM_PORT; + p.fd = ClientDGRAM; + p.timestamp = time(NULL); + p.packet_type = DGRAM_PACKET; + + return(send_packet(&p)); +} diff --git a/source/nameservreply.c b/source/nameservreply.c new file mode 100644 index 00000000000..cd26be5a8b2 --- /dev/null +++ b/source/nameservreply.c @@ -0,0 +1,556 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1996 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Module name: nameservreply.c + + Revision History: + + 14 jan 96: lkcl@pires.co.uk + added multiple workgroup domain master support + + 04 jul 96: lkcl@pires.co.uk + created module nameservreply containing NetBIOS reply functions + +*/ + +#include "includes.h" + +extern int ClientNMB; + +extern int DEBUGLEVEL; + +extern struct in_addr ipgrp; + + +/**************************************************************************** +reply to a name release +****************************************************************************/ +void reply_name_release(struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct in_addr ip; + int nb_flags = nmb->additional->rdata[0]; + BOOL bcast = nmb->header.nm_flags.bcast; + struct name_record *n; + struct subnet_record *d = NULL; + int search = 0; + + putip((char *)&ip,&nmb->additional->rdata[2]); + + DEBUG(3,("Name release on name %s\n", + namestr(&nmb->question.question_name))); + + if (!(d = find_req_subnet(p->ip, bcast))) + { + DEBUG(3,("response packet: bcast %s not known\n", + inet_ntoa(p->ip))); + return; + } + + if (bcast) + search &= FIND_LOCAL; + else + search &= FIND_WINS; + + n = find_name_search(&d, &nmb->question.question_name, + search, ip); + + /* XXXX under what conditions should we reject the removal?? */ + if (n && n->nb_flags == nb_flags) + { + /* success = True; */ + + remove_name(d,n); + n = NULL; + } + + if (bcast) return; + + /* Send a NAME RELEASE RESPONSE */ + send_name_response(p->fd, nmb->header.name_trn_id, NMB_REL, + True, False, + &nmb->question.question_name, nb_flags, 0, ip); +} + + +/**************************************************************************** +send a registration / release response: pos/neg +**************************************************************************/ +void send_name_response(int fd, + int name_trn_id, int opcode, BOOL success, BOOL recurse, + struct nmb_name *reply_name, int nb_flags, int ttl, + struct in_addr ip) +{ + char rdata[6]; + struct packet_struct p; + + int rcode = 0; + + if (success == False) + { + /* NEGATIVE RESPONSE */ + rcode = 6; + } + else if (opcode == NMB_REG && recurse == False) + { + /* END-NODE CHALLENGE REGISTRATION RESPONSE */ + rcode = 0; + } + + rdata[0] = nb_flags; + rdata[1] = 0; + putip(&rdata[2],(char *)&ip); + + p.ip = ip; + p.port = NMB_PORT; + p.fd = fd; + p.timestamp = time(NULL); + p.packet_type = NMB_PACKET; + + reply_netbios_packet(&p,name_trn_id, + rcode,opcode,recurse, + reply_name, 0x20, 0x1, + ttl, + rdata, 6); +} + + +/**************************************************************************** +reply to a reg request +**************************************************************************/ +void reply_name_reg(struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct nmb_name *question = &nmb->question.question_name; + + struct nmb_name *reply_name = question; + + char *qname = question->name; + int qname_type = question->name_type; + + BOOL bcast = nmb->header.nm_flags.bcast; + + int ttl = GET_TTL(nmb->additional->ttl); + int nb_flags = nmb->additional->rdata[0]; + BOOL group = NAME_GROUP(nb_flags); + + struct subnet_record *d = NULL; + struct name_record *n = NULL; + + BOOL success = True; + BOOL secured_redirect = False; + BOOL recurse = True; /* true if samba replies yes/no: false if caller + must challenge the current owner of the unique + name: applies to non-secured WINS server only + */ + + struct in_addr ip, from_ip; + int search = 0; + + putip((char *)&from_ip,&nmb->additional->rdata[2]); + ip = from_ip; + + DEBUG(3,("Name registration for name %s at %s\n", + namestr(question),inet_ntoa(ip))); + + if (group) + { + /* apparently we should return 255.255.255.255 for group queries + (email from MS) */ + ip = ipgrp; + } + + if (!(d = find_req_subnet(p->ip, bcast))) + { + DEBUG(3,("response packet: bcast %s not known\n", + inet_ntoa(p->ip))); + return; + } + + if (bcast) + search &= FIND_LOCAL; + else + search &= FIND_WINS; + + /* see if the name already exists */ + n = find_name_search(&d, question, search, from_ip); + + if (n) + { + if (!group) /* unique names */ + { + if (n->source == SELF || NAME_GROUP(n->nb_flags)) + { + /* no-one can register one of samba's names, nor can they + register a name that's a group name as a unique name */ + + success = False; + } + else if(!ip_equal(ip, n->ip)) + { +#if 0 + /* hm. this unique name doesn't belong to them. */ + + /* XXXX rfc1001.txt says: + * if we are doing non-secured WINS (which is much simpler) then + * we send a message to the person wanting the name saying 'he + * owns this name: i don't want to hear from you ever again + * until you've checked with him if you can have it!'. we then + * abandon the registration. once the person wanting the name + * has checked with the current owner, they will repeat the + * registration packet if the current owner is dead or doesn't + * want the name. + */ + + /* non-secured WINS implementation: caller is responsible + for checking with current owner of name, then getting back + to us... IF current owner no longer owns the unique name */ + + /* XXXX please note also that samba cannot cope with + _receiving_ such redirecting, non-secured registration + packets. code to do this needs to be added. + */ + + secured_redirect = False; + success = False; + recurse = False; + + /* we inform on the current owner to the caller (which is + why it's non-secure */ + + reply_name = &n->name; +#else + /* XXXX rfc1001.txt says: + * if we are doing secured WINS, we must send a Wait-Acknowledge + * packet (WACK) to the person who wants the name, then do a + * name query on the person who currently owns the unique name. + * if the current owner still says they own it, the person who wants + * the name can't have it. if they do not, or are not alive, they can. + */ + + secured_redirect = True; + recurse = False; + + reply_name = &n->name; + +#endif /* 0 */ + + } + else + { + n->ip = ip; + n->death_time = ttl?p->timestamp+ttl*3:0; + DEBUG(3,("%s owner: %s\n",namestr(&n->name),inet_ntoa(n->ip))); + } + } + else + { + /* refresh the name */ + if (n->source != SELF) + { + n->death_time = ttl?p->timestamp + ttl*3:0; + } + } + + /* XXXX bug reported by terryt@ren.pc.athabascau.ca */ + /* names that people have checked for and not found get DNSFAILed. + we need to update the name record if someone then registers */ + + if (n->source == DNSFAIL) + n->source = REGISTER; + + } + else + { + /* add the name to our name/subnet, or WINS, database */ + n = add_netbios_entry(d,qname,qname_type,nb_flags,ttl,REGISTER,ip, + True,!bcast); + } + + /* if samba owns a unique name on a subnet, then it must respond and + disallow the attempted registration. if the registration is + successful by broadcast, only then is there no need to respond + (implicit registration: see rfc1001.txt 15.2.1). + */ + + if (bcast && success) return; + + if (secured_redirect) + { + char rdata[2]; + + /* XXXX luke is confused. RSVAL or SSVAL? assume NMB byte ordering */ + RSSVAL(rdata,0,(nmb->header.opcode&0xf) + ((nb_flags&0xff) << 4)); + + /* XXXX mistake in rfc1002.txt? 4.2.16: NULL is 0xa see 4.2.1.3 + type = 0x0a; see rfc1002.txt 4.2.1.3 + class = 0x01; see rfc1002.txt 4.2.16 + */ + + /* send WAIT ACKNOWLEDGEMENT see rfc1002.txt 4.2.16 */ + reply_netbios_packet(p,nmb->header.name_trn_id, + 0,NMB_WAIT_ACK,False, + reply_name, 0x0a, 0x01, + 15*1000, /* 15 seconds long enough to wait? */ + rdata, 2); + + /* initiate some enquiries to the current owner. */ + queue_netbios_packet(d,ClientNMB,NMB_QUERY, + NAME_REGISTER_CHALLENGE, + reply_name->name,reply_name->name_type,nb_flags,0, + False, False, n->ip, p->ip); + } + else + { + /* Send a NAME REGISTRATION RESPONSE (pos/neg) see rfc1002.txt 4.2.13-14 + or an END-NODE CHALLENGE REGISTRATION RESPONSE see rfc1002.txt 4.2.7 + */ + + send_name_response(p->fd, nmb->header.name_trn_id, NMB_REG, + success, recurse, + reply_name, nb_flags, ttl, ip); + } +} + + +/**************************************************************************** +reply to a name status query +****************************************************************************/ +void reply_name_status(struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + char *qname = nmb->question.question_name.name; + int ques_type = nmb->question.question_name.name_type; + char rdata[MAX_DGRAM_SIZE]; + char *countptr, *buf, *bufend; + int names_added; + struct name_record *n; + struct subnet_record *d = NULL; + + BOOL bcast = nmb->header.nm_flags.bcast; + + if (!(d = find_req_subnet(p->ip, bcast))) + { + DEBUG(3,("Name status req: bcast %s not known\n", + inet_ntoa(p->ip))); + return; + } + + DEBUG(3,("Name status for name %s %s\n", + namestr(&nmb->question.question_name), inet_ntoa(p->ip))); + + n = find_name_search(&d, &nmb->question.question_name, + FIND_SELF|FIND_LOCAL, + p->ip); + + if (!n) return; + + /* XXXX hack, we should calculate exactly how many will fit */ + bufend = &rdata[MAX_DGRAM_SIZE] - 18; + countptr = buf = rdata; + buf += 1; + + names_added = 0; + + for (n = d->namelist ; n && buf < bufend; n = n->next) + { + int name_type = n->name.name_type; + + if (n->source != SELF) continue; + + /* start with first bit of putting info in buffer: the name */ + + bzero(buf,18); + sprintf(buf,"%-15.15s",n->name.name); + strupper(buf); + + /* now check if we want to exclude other workgroup names + from the response. if we don't exclude them, windows clients + get confused and will respond with an error for NET VIEW */ + + if (name_type >= 0x1b && name_type <= 0x20 && + ques_type >= 0x1b && ques_type <= 0x20) + { + if (!strequal(qname, n->name.name)) continue; + } + + /* carry on putting name info in buffer */ + + buf[15] = name_type; + buf[16] = n->nb_flags; + + buf += 18; + + names_added++; + } + + SCVAL(countptr,0,names_added); + + /* XXXXXXX we should fill in more fields of the statistics structure */ + bzero(buf,64); + { + extern int num_good_sends,num_good_receives; + SIVAL(buf,20,num_good_sends); + SIVAL(buf,24,num_good_receives); + } + + SIVAL(buf,46,0xFFB8E5); /* undocumented - used by NT */ + + buf += 64; + + /* Send a POSITIVE NAME STATUS RESPONSE */ + reply_netbios_packet(p,nmb->header.name_trn_id, + 0,0,True, + &nmb->question.question_name, + nmb->question.question_type, + nmb->question.question_class, + 0, + rdata,PTR_DIFF(buf,rdata)); +} + + +/*************************************************************************** +reply to a name query. + +with broadcast name queries: + + - only reply if the query is for one of YOUR names. all other machines on + the network will be doing the same thing (that is, only replying to a + broadcast query if they own it) + NOTE: broadcast name queries should only be sent out by a machine + if they HAVEN'T been configured to use WINS. this is generally bad news + in a wide area tcp/ip network and should be rectified by the systems + administrator. USE WINS! :-) + - the exception to this is if the query is for a Primary Domain Controller + type name (0x1b), in which case, a reply is sent. + + - NEVER send a negative response to a broadcast query. no-one else will! + +with directed name queries: + + - if you are the WINS server, you are expected to respond with either + a negative response, a positive response, or a wait-for-acknowledgement + packet, and then later on a pos/neg response. + +****************************************************************************/ +void reply_name_query(struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct nmb_name *question = &nmb->question.question_name; + int name_type = question->name_type; + BOOL bcast = nmb->header.nm_flags.bcast; + int ttl=0; + int rcode = 0; + int nb_flags = 0; + struct in_addr retip; + char rdata[6]; + struct subnet_record *d = NULL; + BOOL success = True; + struct name_record *n; + + /* directed queries are for WINS server: broadcasts are local SELF queries. + the exception is PDC names. */ + + int search = bcast ? FIND_LOCAL | FIND_SELF : FIND_WINS; + + if (name_type == 0x1b) + { + /* even if it's a broadcast, we don't ignore queries for PDC names */ + search |= FIND_WINS; + search &= ~FIND_SELF; + } + + if (!(d = find_req_subnet(p->ip, bcast))) + { + DEBUG(3,("name query: bcast %s not known\n", + inet_ntoa(p->ip))); + success = False; + } + + DEBUG(3,("Name query ")); + + if (search == 0) + { + /* eh? no criterion for searching database. help! */ + success = False; + } + + if (success && (n = search_for_name(&d,question,p->ip,p->timestamp, search))) + { + /* don't respond to broadcast queries unless the query is for + a name we own or it is for a Primary Domain Controller name */ + + if (bcast && n->source != SELF && name_type != 0x1b) { + if (!lp_wins_proxy() || same_net(p->ip,n->ip,*iface_nmask(p->ip))) { + /* never reply with a negative response to broadcast queries */ + return; + } + } + + /* name is directed query, or it's self, or it's a PDC type name, or + we're replying on behalf of a caller because they are on a different + subnet and cannot hear the broadcast. XXXX lp_wins_proxy should be + switched off in environments where broadcasts are forwarded */ + + /* XXXX note: for proxy servers, we should forward the query on to + another WINS server if the name is not in our database, or we are + not a WINS server ourselves + */ + ttl = n->death_time - p->timestamp; + retip = n->ip; + nb_flags = n->nb_flags; + } + else + { + if (bcast) return; /* never reply negative response to bcasts */ + success = False; + } + + /* if the IP is 0 then substitute my IP */ + if (zero_ip(retip)) retip = *iface_ip(p->ip); + + if (success) + { + rcode = 0; + DEBUG(3,("OK %s\n",inet_ntoa(retip))); + } + else + { + rcode = 3; + DEBUG(3,("UNKNOWN\n")); + } + + if (success) + { + rdata[0] = nb_flags; + rdata[1] = 0; + putip(&rdata[2],(char *)&retip); + } + + reply_netbios_packet(p,nmb->header.name_trn_id, + rcode,0,True, + &nmb->question.question_name, + nmb->question.question_type, + nmb->question.question_class, + ttl, + rdata, success ? 6 : 0); +} + + diff --git a/source/nameservresp.c b/source/nameservresp.c new file mode 100644 index 00000000000..dc7cc63c5ac --- /dev/null +++ b/source/nameservresp.c @@ -0,0 +1,741 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1996 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Revision History: + + Module name: nameservresp.c + + 14 jan 96: lkcl@pires.co.uk + added multiple workgroup domain master support + + 05 jul 96: lkcl@pires.co.uk + created module nameservresp containing NetBIOS response functions + +*/ + +#include "includes.h" + +extern int ClientNMB; + +extern int DEBUGLEVEL; + +extern pstring scope; +extern struct in_addr ipzero; + +#define GET_TTL(ttl) ((ttl)?MIN(ttl,lp_max_ttl()):lp_max_ttl()) + + +/**************************************************************************** + response for a reg release received. samba has asked a WINS server if it + could release a name. + **************************************************************************/ +void response_name_release(struct subnet_record *d, struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + char *name = nmb->question.question_name.name; + int type = nmb->question.question_name.name_type; + + DEBUG(4,("response name release received\n")); + + if (nmb->header.rcode == 0 && nmb->answers->rdata) + { + /* IMPORTANT: see expire_netbios_response_entries() */ + + struct in_addr found_ip; + putip((char*)&found_ip,&nmb->answers->rdata[2]); + + if (ismyip(found_ip)) + { + remove_netbios_name(d,name,type,SELF,found_ip); + } + } + else + { + DEBUG(2,("name release for %s rejected!\n", + namestr(&nmb->question.question_name))); + + /* XXXX do we honestly care if our name release was rejected? + only if samba is issuing the release on behalf of some out-of-sync + server. if it's one of samba's SELF names, we don't care. */ + } +} + + +/**************************************************************************** +response for a reg request received +**************************************************************************/ +void response_name_reg(struct subnet_record *d, struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + char *name = nmb->question.question_name.name; + int type = nmb->question.question_name.name_type; + BOOL bcast = nmb->header.nm_flags.bcast; + + DEBUG(4,("response name registration received!\n")); + + if (nmb->header.rcode == 0 && nmb->answers->rdata) + { + /* IMPORTANT: see expire_netbios_response_entries() */ + + int nb_flags = nmb->answers->rdata[0]; + int ttl = nmb->answers->ttl; + struct in_addr found_ip; + + putip((char*)&found_ip,&nmb->answers->rdata[2]); + + name_register_work(d,name,type,nb_flags,ttl,found_ip,bcast); + } + else + { + DEBUG(1,("name registration for %s rejected!\n", + namestr(&nmb->question.question_name))); + + /* XXXX oh dear. we have problems. must deal with our name having + been rejected: e.g if it was our GROUP(1d) name, we must unbecome + a master browser. */ + + name_unregister_work(d,name,type); + } +} + + +/**************************************************************************** + response from a name query server check. states of type NAME_QUERY_PDC_SRV_CHK, + NAME_QUERY_SRV_CHK, and NAME_QUERY_FIND_MST dealt with here. + ****************************************************************************/ +static void response_server_check(struct nmb_name *ans_name, + struct response_record *n, struct subnet_record *d) +{ + /* issue another state: this time to do a name status check */ + + enum state_type cmd = (n->state == NAME_QUERY_PDC_SRV_CHK) ? + NAME_STATUS_PDC_SRV_CHK : NAME_STATUS_SRV_CHK; + + /* 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->send_ip,n->reply_to_ip); +} + + +/**************************************************************************** + interpret a node status response. this is pretty hacked: we need two bits of + info. a) the name of the workgroup b) the name of the server. it will also + add all the names it finds into the namelist. +****************************************************************************/ +static BOOL interpret_node_status(struct subnet_record *d, + char *p, struct nmb_name *name,int t, + char *serv_name, struct in_addr ip, BOOL bcast) +{ + int level = t==0x20 ? 4 : 0; + int numnames = CVAL(p,0); + BOOL found = False; + + DEBUG(level,("received %d names\n",numnames)); + + p += 1; + + if (serv_name) *serv_name = 0; + + while (numnames--) + { + char qname[17]; + int type; + fstring flags; + int nb_flags; + + BOOL group = False; + BOOL add = False; + + *flags = 0; + + StrnCpy(qname,p,15); + type = CVAL(p,15); + nb_flags = p[16]; + trim_string(qname,NULL," "); + + p += 18; + + if (NAME_GROUP (nb_flags)) { strcat(flags," "); group=True;} + if (NAME_BFLAG (nb_flags)) { strcat(flags,"B "); } + if (NAME_PFLAG (nb_flags)) { strcat(flags,"P "); } + if (NAME_MFLAG (nb_flags)) { strcat(flags,"M "); } + if (NAME__FLAG (nb_flags)) { strcat(flags,"_ "); } + if (NAME_DEREG (nb_flags)) { strcat(flags," "); } + if (NAME_CONFLICT (nb_flags)) { strcat(flags," "); add=True;} + if (NAME_ACTIVE (nb_flags)) { strcat(flags," "); add=True; } + if (NAME_PERMANENT(nb_flags)) { strcat(flags," "); add=True;} + + /* might as well update our namelist while we're at it */ + if (add) + { + struct in_addr nameip; + enum name_source src; + + if (ismyip(ip)) { + nameip = ipzero; + src = SELF; + } else { + nameip = ip; + src = STATUS_QUERY; + } + add_netbios_entry(d,qname,type,nb_flags,2*60*60,src,nameip,True,bcast); + } + + /* we want the server name */ + if (serv_name && !*serv_name && !group && t == 0) + { + StrnCpy(serv_name,qname,15); + serv_name[15] = 0; + } + + /* looking for a name and type? */ + if (name && !found && (t == type)) + { + /* take a guess at some of the name types we're going to ask for. + evaluate whether they are group names or no... */ + if (((t == 0x1b || t == 0x1d ) && !group) || + ((t == 0x20 || t == 0x1c || t == 0x1e) && group)) + { + found = True; + make_nmb_name(name,qname,type,scope); + } + } + + DEBUG(level,("\t%s(0x%x)\t%s\n",qname,type,flags)); + } + DEBUG(level,("num_good_sends=%d num_good_receives=%d\n", + IVAL(p,20),IVAL(p,24))); + return found; +} + + +/**************************************************************************** + response from a name status check. states of type NAME_STATUS_PDC_SRV_CHK + and NAME_STATUS_SRV_CHK dealt with here. + ****************************************************************************/ +static void response_name_status_check(struct in_addr ip, + struct nmb_packet *nmb, BOOL bcast, + struct response_record *n, struct subnet_record *d) +{ + /* NMB_STATUS arrives: contains workgroup name and server name required. + amongst other things. */ + + struct nmb_name name; + fstring serv_name; + + if (interpret_node_status(d,nmb->answers->rdata, + &name,name.name_type,serv_name,ip,bcast)) + { + if (*serv_name) + { + sync_server(n->state,serv_name, + name.name,name.name_type, n->send_ip); + } + } + else + { + DEBUG(1,("No 0x1d name type in interpret_node_status()\n")); + } +} + + +/**************************************************************************** + response from a name query for secured WINS registration. a state of + NAME_REGISTER_CHALLENGE is dealt with here. + ****************************************************************************/ +static void response_name_query_register(struct nmb_packet *nmb, + struct nmb_name *ans_name, + struct response_record *n, struct subnet_record *d) +{ + struct in_addr register_ip; + BOOL new_owner; + + DEBUG(4, ("Name query at %s ip %s - ", + namestr(&n->name), inet_ntoa(n->send_ip))); + + if (!name_equal(&n->name, ans_name)) + { + /* someone gave us the wrong name as a reply. oops. */ + /* XXXX should say to them 'oi! release that name!' */ + + DEBUG(4,("unexpected name received: %s\n", namestr(ans_name))); + return; + } + + if (nmb->header.rcode == 0 && nmb->answers->rdata) + { + /* we had sent out a name query to the current owner + of a name because someone else wanted it. now they + have responded saying that they still want the name, + so the other host can't have it. + */ + + /* first check all the details are correct */ + + int nb_flags = nmb->answers->rdata[0]; + struct in_addr found_ip; + + putip((char*)&found_ip,&nmb->answers->rdata[2]); + + if (nb_flags != n->nb_flags) + { + /* someone gave us the wrong nb_flags as a reply. oops. */ + /* XXXX should say to them 'oi! release that name!' */ + + DEBUG(4,("expected nb_flags: %d\n", n->nb_flags)); + DEBUG(4,("unexpected nb_flags: %d\n", nb_flags)); + return; + } + + if (!ip_equal(n->send_ip, found_ip)) + { + /* someone gave us the wrong ip as a reply. oops. */ + /* XXXX should say to them 'oi! release that name!' */ + + DEBUG(4,("expected ip: %s\n", inet_ntoa(n->send_ip))); + DEBUG(4,("unexpected ip: %s\n", inet_ntoa(found_ip))); + return; + } + + DEBUG(4, (" OK: %s\n", inet_ntoa(found_ip))); + + /* fine: now tell the other host they can't have the name */ + register_ip = n->send_ip; + new_owner = False; + } + else + { + DEBUG(4, (" NEGATIVE RESPONSE!\n")); + + /* the owner didn't want the name: the other host can have it */ + register_ip = n->reply_to_ip; + new_owner = True; + } + + /* register the old or the new owners' ip */ + add_netbios_entry(d, ans_name->name, ans_name->name_type, + n->nb_flags,GET_TTL(0),REGISTER, + register_ip,False,True); + + /* reply yes or no to the host that requested the name */ + send_name_response(n->fd, n->response_id, NMB_REG, + new_owner, True, + &n->name, n->nb_flags, GET_TTL(0), n->reply_to_ip); +} + + +/**************************************************************************** + response from a name query to sync browse lists or to update our netbios + entry. states of type NAME_QUERY_SYNC and NAME_QUERY_CONFIRM + ****************************************************************************/ +static void response_name_query_sync(struct nmb_packet *nmb, + struct nmb_name *ans_name, BOOL bcast, + struct response_record *n, struct subnet_record *d) +{ + DEBUG(4, ("Name query at %s ip %s - ", + namestr(&n->name), inet_ntoa(n->send_ip))); + + if (!name_equal(&n->name, ans_name)) + { + /* someone gave us the wrong name as a reply. oops. */ + DEBUG(4,("unexpected name received: %s\n", namestr(ans_name))); + return; + } + + if (nmb->header.rcode == 0 && nmb->answers->rdata) + { + int nb_flags = nmb->answers->rdata[0]; + struct in_addr found_ip; + + putip((char*)&found_ip,&nmb->answers->rdata[2]); + + if (!ip_equal(n->send_ip, found_ip)) + { + /* someone gave us the wrong ip as a reply. oops. */ + DEBUG(4,("expected ip: %s\n", inet_ntoa(n->send_ip))); + DEBUG(4,("unexpected ip: %s\n", inet_ntoa(found_ip))); + return; + } + + DEBUG(4, (" OK: %s\n", inet_ntoa(found_ip))); + + if (n->state == NAME_QUERY_SYNC) + { + struct work_record *work = NULL; + if ((work = find_workgroupstruct(d, ans_name->name, False))) + { + /* the server is there: sync quick before it (possibly) dies! */ + sync_browse_lists(d, work, ans_name->name, ans_name->name_type, + found_ip); + } + } + else + { + /* update our netbios name list (re-register it if necessary) */ + add_netbios_entry(d, ans_name->name, ans_name->name_type, + nb_flags,GET_TTL(0),REGISTER, + found_ip,False,!bcast); + } + } + else + { + DEBUG(4, (" NEGATIVE RESPONSE!\n")); + + if (n->state == NAME_QUERY_CONFIRM) + { + /* XXXX remove_netbios_entry()? */ + /* lots of things we ought to do, here. if we get here, + then we're in a mess: our name database doesn't match + reality. sort it out + */ + remove_netbios_name(d,n->name.name, n->name.name_type, + REGISTER,n->send_ip); + } + } +} + + +/**************************************************************************** + report the response record type + ****************************************************************************/ +static void debug_rr_type(int rr_type) +{ + switch (rr_type) + { + case NMB_STATUS: DEBUG(3,("Name status ")); break; + case NMB_QUERY : DEBUG(3,("Name query ")); break; + case NMB_REG : DEBUG(3,("Name registration ")); break; + case NMB_REL : DEBUG(3,("Name release ")); break; + default : DEBUG(1,("wrong response packet type received")); break; + } +} + +/**************************************************************************** + report the response record nmbd state + ****************************************************************************/ +static void debug_state_type(int state) +{ + /* report the state type to help debugging */ + switch (state) + { + case NAME_QUERY_PDC_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_PDC_SRV_CHK: DEBUG(4,("NAME_STAT_MST_CHK\n")); break; + case NAME_STATUS_SRV_CHK : DEBUG(4,("NAME_STATUS_SRV_CHK\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_REGISTER_CHALLENGE: DEBUG(4,("NAME_REGISTER_CHALLENGE\n")); break; + case NAME_RELEASE : DEBUG(4,("NAME_RELEASE\n")); break; + case NAME_QUERY_CONFIRM : DEBUG(4,("NAME_QUERY_CONFIRM\n")); break; + case NAME_QUERY_SYNC : DEBUG(4,("NAME_QUERY_SYNC\n")); break; + default: break; + } +} + +/**************************************************************************** + report any problems with the fact that a response has been received. + + (responses for certain types of operations are only expected from one host) + ****************************************************************************/ +static BOOL response_problem_check(struct response_record *n, + struct nmb_packet *nmb, char *qname) +{ + switch (nmb->answers->rr_type) + { + case NMB_REL: + { + if (n->num_msgs > 1) + { + DEBUG(1,("more than one release name response received!\n")); + return True; + } + break; + } + + case NMB_REG: + { + if (n->num_msgs > 1) + { + DEBUG(1,("more than one register name response received!\n")); + return True; + } + break; + } + + case NMB_QUERY: + { + if (n->num_msgs > 1) + { + if (nmb->header.rcode == 0 && nmb->answers->rdata) + { + int nb_flags = nmb->answers->rdata[0]; + + if ((!NAME_GROUP(nb_flags))) + { + /* oh dear. more than one person responded to a unique name. + there is either a network problem, a configuration problem + or a server is mis-behaving */ + + /* XXXX mark the name as in conflict, and then let the + person who just responded know that they must also mark it + as in conflict, and therefore must NOT use it. + see rfc1001.txt 15.1.3.5 */ + + /* this may cause problems for some early versions of nmbd */ + + switch (n->state) + { + case NAME_QUERY_FIND_MST: + { + /* query for ^1^2__MSBROWSE__^2^1 expect lots of responses */ + return False; + } + case NAME_QUERY_PDC_SRV_CHK: + case NAME_QUERY_SRV_CHK: + case NAME_QUERY_MST_CHK: + { + if (!strequal(qname,n->name.name)) + { + /* one subnet, one master browser per workgroup */ + /* XXXX force an election? */ + + DEBUG(3,("more than one master browser replied!\n")); + return True; + } + break; + } + default: break; + } + DEBUG(3,("Unique Name conflict detected!\n")); + return True; + } + } + else + { + /* we have received a negative reply, having already received + at least one response (pos/neg). something's really wrong! */ + + DEBUG(3,("wierd name query problem detected!\n")); + return True; + } + } + } + } + return False; +} + +/**************************************************************************** + check that the response received is compatible with the response record + ****************************************************************************/ +static BOOL response_compatible(struct response_record *n, + struct nmb_packet *nmb) +{ + switch (n->state) + { + case NAME_RELEASE: + { + if (nmb->answers->rr_type != NMB_REL) + { + DEBUG(1,("Name release reply has wrong answer rr_type\n")); + return False; + } + break; + } + + case NAME_REGISTER: + { + if (nmb->answers->rr_type != NMB_REG) + { + DEBUG(1,("Name register reply has wrong answer rr_type\n")); + return False; + } + break; + } + + case NAME_REGISTER_CHALLENGE: /* this is a query: we then do a register */ + case NAME_QUERY_CONFIRM: + case NAME_QUERY_SYNC: + case NAME_QUERY_PDC_SRV_CHK: + case NAME_QUERY_SRV_CHK: + case NAME_QUERY_FIND_MST: + case NAME_QUERY_MST_CHK: + { + if (nmb->answers->rr_type != NMB_QUERY) + { + DEBUG(1,("Name query reply has wrong answer rr_type\n")); + return False; + } + break; + } + + case NAME_STATUS_PDC_SRV_CHK: + case NAME_STATUS_SRV_CHK: + { + if (nmb->answers->rr_type != NMB_STATUS) + { + DEBUG(1,("Name status reply has wrong answer rr_type\n")); + return False; + } + break; + } + + default: + { + DEBUG(1,("unknown state type received in response_netbios_packet\n")); + return False; + } + } + return True; +} + + +/**************************************************************************** + process the response packet received + ****************************************************************************/ +static void response_process(struct subnet_record *d, struct packet_struct *p, + struct response_record *n, struct nmb_packet *nmb, + BOOL bcast, struct nmb_name *ans_name) +{ + switch (n->state) + { + case NAME_RELEASE: + { + response_name_release(d, p); + break; + } + + case NAME_REGISTER: + { + response_name_reg(d, p); + break; + } + + case NAME_REGISTER_CHALLENGE: + { + response_name_query_register(nmb, ans_name, n, d); + break; + } + + case NAME_QUERY_PDC_SRV_CHK: + case NAME_QUERY_SRV_CHK: + case NAME_QUERY_FIND_MST: + { + response_server_check(ans_name, n, d); + break; + } + + case NAME_STATUS_PDC_SRV_CHK: + case NAME_STATUS_SRV_CHK: + { + 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->send_ip))); + break; + } + + default: + { + DEBUG(1,("unknown state type received in response_netbios_packet\n")); + break; + } + } +} + + +/**************************************************************************** + response from a netbios packet. + ****************************************************************************/ +void response_netbios_packet(struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct nmb_name *question = &nmb->question.question_name; + struct nmb_name *ans_name = NULL; + char *qname = question->name; + BOOL bcast = nmb->header.nm_flags.bcast; + struct response_record *n; + struct subnet_record *d = NULL; + + if (!(n = find_response_record(&d,nmb->header.name_trn_id))) { + DEBUG(2,("unknown netbios response (received late or from nmblookup?)\n")); + return; + } + + if (!d) + { + DEBUG(2,("response packet: subnet %s not known\n", inet_ntoa(p->ip))); + return; + } + + if (!same_net(d->bcast_ip, d->mask_ip, p->ip)) /* copes with WINS 'subnet' */ + { + DEBUG(2,("response from %s. ", inet_ntoa(p->ip))); + DEBUG(2,("expected on subnet %s. hmm.\n", inet_ntoa(d->bcast_ip))); + return; + } + + if (nmb->answers == NULL) + { + /* hm. the packet received was a response, but with no answer. wierd! */ + DEBUG(2,("NMB packet response from %s (bcast=%s) - UNKNOWN\n", + inet_ntoa(p->ip), BOOLSTR(bcast))); + return; + } + + ans_name = &nmb->answers->rr_name; + DEBUG(3,("response for %s from %s (bcast=%s)\n", + namestr(ans_name), inet_ntoa(p->ip), BOOLSTR(bcast))); + + debug_rr_type(nmb->answers->rr_type); + + n->num_msgs++; /* count number of responses received */ + n->repeat_count = 0; /* don't resend: see expire_netbios_packets() */ + + debug_state_type(n->state); + + /* problem checking: multiple responses etc */ + if (response_problem_check(n, nmb, qname)) + return; + + /* now check whether the 'state' has received the correct type of response */ + if (!response_compatible(n, nmb)) + return; + + /* now deal with the current state */ + response_process(d, p, n, nmb, bcast, ans_name); +} + + -- cgit