added the recently shuffled and updated source files missed in the
authorSamba Release Account <samba-bugs@samba.org>
Sun, 7 Jul 1996 13:29:56 +0000 (13:29 +0000)
committerSamba Release Account <samba-bugs@samba.org>
Sun, 7 Jul 1996 13:29:56 +0000 (13:29 +0000)
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
(This used to be commit d78b319062144d14a54408fce77ccc313ad58ee3)

source3/include/proto.h
source3/namedb.c
source3/namedbname.c [new file with mode: 0644]
source3/namedbresp.c [new file with mode: 0644]
source3/namedbserver.c [new file with mode: 0644]
source3/namedbsubnet.c [new file with mode: 0644]
source3/namedbwork.c [new file with mode: 0644]
source3/namepacket.c [new file with mode: 0644]
source3/nameservreply.c [new file with mode: 0644]
source3/nameservresp.c [new file with mode: 0644]

index 6f92de8586aa60cc735c1251e264168bb26b0d0c..437a7e25b9afc8953492a08ad765f5b4c8963ed3 100644 (file)
@@ -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);
index 305dfb4476643a16bb6540800914f0e3c4161a02..96f64393c795fc4590a345acbaccb0e6e484eca2 100644 (file)
@@ -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/source3/namedbname.c b/source3/namedbname.c
new file mode 100644 (file)
index 0000000..3bff6da
--- /dev/null
@@ -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/source3/namedbresp.c b/source3/namedbresp.c
new file mode 100644 (file)
index 0000000..a9abc5d
--- /dev/null
@@ -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/source3/namedbserver.c b/source3/namedbserver.c
new file mode 100644 (file)
index 0000000..74c7a96
--- /dev/null
@@ -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/source3/namedbsubnet.c b/source3/namedbsubnet.c
new file mode 100644 (file)
index 0000000..5ede7b0
--- /dev/null
@@ -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/source3/namedbwork.c b/source3/namedbwork.c
new file mode 100644 (file)
index 0000000..7937aa4
--- /dev/null
@@ -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/source3/namepacket.c b/source3/namepacket.c
new file mode 100644 (file)
index 0000000..38b8ed0
--- /dev/null
@@ -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/source3/nameservreply.c b/source3/nameservreply.c
new file mode 100644 (file)
index 0000000..cd26be5
--- /dev/null
@@ -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/source3/nameservresp.c b/source3/nameservresp.c
new file mode 100644 (file)
index 0000000..dc7cc63
--- /dev/null
@@ -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> "); 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,"<DEREGISTERING> "); }
+      if (NAME_CONFLICT (nb_flags)) { strcat(flags,"<CONFLICT> "); add=True;}
+      if (NAME_ACTIVE   (nb_flags)) { strcat(flags,"<ACTIVE> "); add=True; }
+      if (NAME_PERMANENT(nb_flags)) { strcat(flags,"<PERMANENT> "); 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);
+}
+
+