fixed conflict between two variables called d
[samba.git] / source3 / nameresp.c
index 78e6274f8ca8842abdd3455adbeadf0f7bf0a692..c79c30d46f6001f5d68956f4935f45559a697e99 100644 (file)
 */
 
 #include "includes.h"
-#include "loadparm.h"
 
 extern int ClientNMB;
 extern int ClientDGRAM;
 
-/* this is our initiated name query response database */
-struct name_response_record *nameresponselist = NULL;
+extern struct subnet_record *subnetlist;
 
 extern int DEBUGLEVEL;
 
@@ -36,24 +34,42 @@ BOOL CanRecurse = True;
 extern pstring scope;
 extern pstring myname;
 extern struct in_addr ipzero;
+extern struct in_addr ipgrp;
+
+int num_response_packets = 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;
+}
 
 
 /***************************************************************************
   add an initated name query  into the list
   **************************************************************************/
-extern void add_response_record(struct name_response_record *n)
+static void add_response_record(struct subnet_record *d,
+                               struct response_record *n)
 {
-  struct name_response_record *n2;
+  struct response_record *n2;
+
+  if (!d) return;
 
-  if (!nameresponselist)
+  if (!d->responselist)
     {
-      nameresponselist = n;
+      d->responselist = n;
       n->prev = NULL;
       n->next = NULL;
       return;
     }
   
-  for (n2 = nameresponselist; n2->next; n2 = n2->next) ;
+  for (n2 = d->responselist; n2->next; n2 = n2->next) ;
   
   n2->next = n;
   n->next = NULL;
@@ -61,112 +77,125 @@ extern void add_response_record(struct name_response_record *n)
 }
 
 
-/*******************************************************************
-  remove old name response entries
-  ******************************************************************/
-void expire_netbios_response_entries(time_t t)
+/***************************************************************************
+  deals with an entry before it dies
+  **************************************************************************/
+static void dead_netbios_entry(struct subnet_record *d,
+                               struct response_record *n)
 {
-  struct name_response_record *n;
-  struct name_response_record *nextn;
+  DEBUG(3,("Removing dead netbios entry for %s %s (num_msgs=%d)\n",
+          inet_ntoa(n->to_ip), namestr(&n->name), n->num_msgs));
 
-  for (n = nameresponselist; n; n = nextn)
+  switch (n->state)
+  {
+    case NAME_QUERY_CONFIRM:
     {
-      if (n->start_time < t)
+               if (!lp_wins_support()) return; /* only if we're a WINS server */
+
+               if (n->num_msgs == 0)
+        {
+                       /* oops. name query had no response. check that the name is
+                          unique and then remove it from our WINS database */
+
+                       /* IMPORTANT: see query_refresh_names() */
+
+                       if ((!NAME_GROUP(n->nb_flags)))
+                       {
+                               struct subnet_record *d1 = find_subnet(ipgrp);
+                               if (d1)
+                               {
+                                       /* remove the name that had been registered with us,
+                                          and we're now getting no response when challenging.
+                                          see rfc1001.txt 15.5.2
+                                        */
+                                       remove_netbios_name(d1, n->name.name, n->name.name_type,
+                                                                       REGISTER, n->to_ip);
+                               }
+                       }
+               }
+               break;
+    }
+
+       case NAME_QUERY_MST_CHK:
        {
-         DEBUG(3,("Removing dead name query for %s %s (num_msgs=%d)\n",
-                  inet_ntoa(n->to_ip), namestr(&n->name), n->num_msgs));
+         /* if no response received, the master browser must have gone
+                down on that subnet, without telling anyone. */
 
-         if (n->cmd_type == CHECK_MASTER)
-           {
-             /* if no response received, the master browser must have gone */
-             if (n->num_msgs == 0)
-               browser_gone(n->name.name, n->to_ip);
-           }
-         
-         nextn = n->next;
-         
-         if (n->prev) n->prev->next = n->next;
-         if (n->next) n->next->prev = n->prev;
-         
-         if (nameresponselist == n) nameresponselist = n->next; 
-         
-         free(n);
+         /* IMPORTANT: see response_netbios_packet() */
+
+         if (n->num_msgs == 0)
+                 browser_gone(n->name.name, n->to_ip);
+         break;
        }
-      else
+
+       case NAME_RELEASE:
        {
-         nextn = n->next;
+         /* if no response received, it must be OK for us to release the
+                name. nobody objected (including a potentially dead or deaf
+                WINS server) */
+
+         /* IMPORTANT: see response_name_release() */
+
+         if (ismyip(n->to_ip))
+         {
+               remove_netbios_name(d,n->name.name,n->name.name_type,SELF,n->to_ip);
+         }
+         if (!n->bcast)
+         {
+                DEBUG(1,("WINS server did not respond to name release!\n"));
+         }
+         break;
        }
-    }
-}
 
+       case NAME_REGISTER:
+       {
+         /* if no response received, and we are using a broadcast registration
+                method, it must be OK for us to register the name: nobody objected 
+                on that subnet. if we are using a WINS server, then the WINS
+                server must be dead or deaf.
+          */
+         if (n->bcast)
+         {
+               /* broadcast method: implicit acceptance of the name registration
+                  by not receiving any objections. */
 
-/****************************************************************************
-  reply to a netbios name packet 
-  ****************************************************************************/
-void reply_netbios_packet(struct packet_struct *p1,int trn_id,int rcode,int opcode,
-                         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";
-  
-  p = *p1;
+               /* IMPORTANT: see response_name_reg() */
 
-  if (rr_type == NMB_STATUS) packet_type = "nmb_status";
-  if (rr_type == NMB_QUERY ) packet_type = "nmb_query";
-  if (rr_type == NMB_REG   ) packet_type = "nmb_reg";
-  if (rr_type == NMB_REL   ) packet_type = "nmb_rel";
-  
-  DEBUG(4,("replying netbios packet: %s %s\n",
-          packet_type, namestr(rr_name), inet_ntoa(p.ip)));
+               enum name_source source = ismyip(n->to_ip) ? SELF : REGISTER;
 
-  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 = True;
-  nmb->header.nm_flags.recursion_desired = True;
-  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);
+               add_netbios_entry(d,n->name.name,n->name.name_type,
+                               n->nb_flags, n->ttl, source,n->to_ip, True,!n->bcast);
+         }
+         else
+         {
+               /* XXXX oops. this is where i wish this code could retry DGRAM
+                  packets. we directed a name registration at a WINS server, and
+                  received no response. rfc1001.txt states that after retrying,
+                  we should assume the WINS server is dead, and fall back to
+                  broadcasting. */
+               
+                DEBUG(1,("WINS server did not respond to name registration!\n"));
+         }
+         break;
+       }
+
+       default:
+       {
+         /* nothing to do but delete the dead expected-response structure */
+         /* this is normal. */
+         break;
+       }
+  }
 }
 
 
 /****************************************************************************
   initiate a netbios packet
   ****************************************************************************/
-uint16 initiate_netbios_packet(int fd,int quest_type,char *name,int name_type,
-                              int nb_flags,BOOL bcast,BOOL recurse,
-                              struct in_addr to_ip)
+static 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;
@@ -174,6 +203,8 @@ uint16 initiate_netbios_packet(int fd,int quest_type,char *name,int name_type,
   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; }
@@ -182,15 +213,15 @@ uint16 initiate_netbios_packet(int fd,int quest_type,char *name,int name_type,
   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 False;
+  if (opcode == -1) return;
 
   bzero((char *)&p,sizeof(p));
 
-  if (!name_trn_id) name_trn_id = (time(NULL)%(unsigned)0x7FFF) + 
-    (getpid()%(unsigned)100);
-  name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF;
+  update_name_trn_id();
+
+  if (*id == 0xffff) *id = name_trn_id; /* allow resending with same id */
 
-  nmb->header.name_trn_id = name_trn_id;
+  nmb->header.name_trn_id = *id;
   nmb->header.opcode = opcode;
   nmb->header.response = False;
   nmb->header.nm_flags.bcast = bcast;
@@ -230,10 +261,120 @@ uint16 initiate_netbios_packet(int fd,int quest_type,char *name,int name_type,
   p.timestamp = time(NULL);
   p.packet_type = NMB_PACKET;
   
-  if (!send_packet(&p)) 
-    return(0);
+  if (!send_packet(&p)) *id = 0xffff;
+  
+  return;
+}
+
+
+/*******************************************************************
+  remove old name response entries
+  XXXX retry code needs to be added, including a retry wait period and a count
+       see name_query() and name_status() for suggested implementation.
+  ******************************************************************/
+void expire_netbios_response_entries()
+{
+  struct response_record *n;
+  struct response_record *nextn;
+  struct subnet_record *d;
+
+  for (d = subnetlist; d; d = d->next)
+   for (n = d->responselist; n; n = nextn)
+    {
+      if (n->repeat_time < time(NULL))
+         {
+                 if (n->repeat_count > 0)
+                 {
+                       /* resend the entry */
+                       initiate_netbios_packet(&n->response_id, n->fd, n->quest_type,
+                                               n->name.name, n->name.name_type,
+                                     n->nb_flags, n->bcast, n->recurse, n->to_ip);
+
+            n->repeat_time += n->repeat_interval; /* XXXX ms needed */
+            n->repeat_count--;
+                 }
+                 else
+                 {
+                         dead_netbios_entry(d,n);
+
+                         nextn = n->next;
+                         
+                         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--;
+
+                         continue;
+                  }
+         }
+         nextn = n->next;
+    }
+}
+
+
+/****************************************************************************
+  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";
+  
+  p = *p1;
+
+  if (rr_type == NMB_STATUS) packet_type = "nmb_status";
+  if (rr_type == NMB_QUERY ) packet_type = "nmb_query";
+  if (rr_type == NMB_REG   ) packet_type = "nmb_reg";
+  if (rr_type == NMB_REL   ) packet_type = "nmb_rel";
+  
+  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 = True;
+  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);
   
-  return(name_trn_id);
+  send_packet(&p);
 }
 
 
@@ -242,10 +383,17 @@ uint16 initiate_netbios_packet(int fd,int quest_type,char *name,int name_type,
   name server instead, if it exists. if wins is false, and there has been no
   WINS server specified, the packet will NOT be sent.
   ****************************************************************************/
-void queue_netbios_pkt_wins(int fd,int quest_type,enum cmd_type cmd,
-                           char *name,int name_type,int nb_flags,
+void 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)
 {
+  /* XXXX note: please see rfc1001.txt section 10 for details on this
+     function: it is currently inappropriate to use this - it will do
+     for now - once there is a clarification of B, M and P nodes and
+     which one samba is supposed to be
+   */
+
   if ((!lp_wins_support()) && (*lp_wins_server()))
     {
       /* samba is not a WINS server, and we are using a WINS server */
@@ -267,37 +415,45 @@ void queue_netbios_pkt_wins(int fd,int quest_type,enum cmd_type cmd,
 
   if (zero_ip(to_ip)) return;
 
-  queue_netbios_packet(fd, quest_type, cmd
-                      name, name_type, nb_flags,
+  queue_netbios_packet(d,fd, quest_type, state
+                      name, name_type, nb_flags, ttl,
                       bcast, recurse, to_ip);
 }
 
 /****************************************************************************
   create a name query response record
   **************************************************************************/
-static struct name_response_record *make_name_query_record(
-                                                          enum cmd_type cmd,int id,int fd,
-                                                          char *name,int type,
-                                                          BOOL bcast,BOOL recurse,
-                                                          struct in_addr ip)
+static 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 ip)
 {
-  struct name_response_record *n;
+  struct response_record *n;
        
   if (!name || !name[0]) return NULL;
        
-  if (!(n = (struct name_response_record *)malloc(sizeof(*n)))) 
+  if (!(n = (struct response_record *)malloc(sizeof(*n)))) 
     return(NULL);
 
   n->response_id = id;
-  n->cmd_type = cmd;
+  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->to_ip = ip;
-  n->start_time = time(NULL);
+
+  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;
 
+  num_response_packets++; /* count of total number of packets still around */
+
   return n;
 }
 
@@ -308,37 +464,53 @@ static struct name_response_record *make_name_query_record(
   master browsers (WORKGROUP(1d or 1b) or __MSBROWSE__(1)) to get
   complete lists across a wide area network
   ****************************************************************************/
-void queue_netbios_packet(int fd,int quest_type,enum cmd_type cmd,char *name,
-                         int name_type,int nb_flags,BOOL bcast,BOOL recurse,
-                         struct in_addr to_ip)
+void 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)
 {
-  uint16 id = initiate_netbios_packet(fd, quest_type, name, name_type,
+  struct in_addr wins_ip = ipgrp;
+  struct response_record *n;
+  uint16 id = 0xffff;
+
+  /* ha ha. no. do NOT broadcast to 255.255.255.255: it's a pseudo address */
+  if (ip_equal(wins_ip, to_ip)) return;
+
+  initiate_netbios_packet(&id, fd, quest_type, name, name_type,
                                      nb_flags, bcast, recurse, to_ip);
-  struct name_response_record *n;
 
-  if (id == 0) return;
+  if (id == 0xffff) return;
   
-  if ((n = 
-       make_name_query_record(cmd,id,fd,name,name_type,bcast,recurse,to_ip)))
+  if ((n = make_response_queue_record(state,id,fd,
+                                               quest_type,name,name_type,nb_flags,ttl,
+                                               bcast,recurse,to_ip)))
     {
-      add_response_record(n);
+      add_response_record(d,n);
     }
 }
 
 
 /****************************************************************************
-  find a response in the name query response list
+  find a response in a subnet's name query response list. 
   **************************************************************************/
-struct name_response_record *find_name_query(uint16 id)
-{   
-  struct name_response_record *n;
+struct response_record *find_response_record(struct subnet_record **d,
+                               uint16 id)
+{  
+  struct response_record *n;
 
-  for (n = nameresponselist; n; n = n->next)
+  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;
+      if (n->response_id == id) {
+         return n;
       }
     }
+  }
+
+  *d = NULL;
 
   return NULL;
 }
@@ -412,10 +584,12 @@ void listen_for_packets(BOOL run_election)
   FD_SET(ClientNMB,&fds);
   FD_SET(ClientDGRAM,&fds);
 
-  /* during elections we need to send election packets at one
-     second intervals */
+  /* 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 ? 1 : NMBD_SELECT_LOOP;
+  timeout.tv_sec = (run_election||num_response_packets) ? 1 : NMBD_SELECT_LOOP;
   timeout.tv_usec = 0;
 
   selrtn = sys_select(&fds,&timeout);
@@ -464,8 +638,9 @@ 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.
 ****************************************************************************/
-BOOL interpret_node_status(char *p, struct nmb_name *name,int t,
-                          char *serv_name, struct in_addr ip)
+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);
@@ -519,7 +694,7 @@ BOOL interpret_node_status(char *p, struct nmb_name *name,int t,
            nameip = ip;
            src = STATUS_QUERY;
          }
-         add_netbios_entry(qname,type,nb_flags,2*60*60,src,nameip,True);
+         add_netbios_entry(d,qname,type,nb_flags,2*60*60,src,nameip,True,bcast);
        } 
 
       /* we want the server name */
@@ -562,16 +737,22 @@ BOOL send_mailslot_reply(char *mailslot,int fd,char *buf,int len,char *srcname,
 {
   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.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 */