includes.h: Added USE_SETSID for SGI.
[kai/samba.git] / source / nameservreply.c
index cd26be5a8b2cf9a31b69b7b99c87d7a181160527..9e46b80303922a8efde1aa9f0a48e96e0b8fc2a2 100644 (file)
@@ -2,7 +2,7 @@
    Unix SMB/Netbios implementation.
    Version 1.9.
    NBT netbios routines and daemon - version 2
-   Copyright (C) Andrew Tridgell 1994-1996
+   Copyright (C) Andrew Tridgell 1994-1997
    
    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
@@ -36,64 +36,12 @@ 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);
-}
-
+extern struct in_addr wins_ip;
 
 /****************************************************************************
 send a registration / release response: pos/neg
 **************************************************************************/
-void send_name_response(int fd,
+static void send_name_response(int fd, struct in_addr from_ip,
                                int name_trn_id, int opcode, BOOL success, BOOL recurse,
                                struct nmb_name *reply_name, int nb_flags, int ttl,
                                struct in_addr ip)
@@ -118,19 +66,93 @@ void send_name_response(int fd,
   rdata[1] = 0;
   putip(&rdata[2],(char *)&ip);
   
-  p.ip = ip;
+  p.ip = from_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,
+                      rcode,opcode,opcode,recurse,
                       reply_name, 0x20, 0x1,
                       ttl, 
                       rdata, 6);
 }
 
+/****************************************************************************
+  add a netbios entry. respond to the (possibly new) owner.
+  **************************************************************************/
+void add_name_respond(struct subnet_record *d, int fd, struct in_addr from_ip,
+                               uint16 response_id,
+                               struct nmb_name *name,
+                               int nb_flags, int ttl, struct in_addr register_ip,
+                               BOOL new_owner, struct in_addr reply_to_ip)
+{
+  /* register the old or the new owners' ip */
+  add_netbios_entry(d,name->name,name->name_type,
+                    nb_flags,ttl,REGISTER,register_ip,False,True);
+
+  /* reply yes or no to the host that requested the name */
+  send_name_response(fd,from_ip, response_id, NMB_REG,
+                     new_owner, False,
+                     name, nb_flags, ttl, reply_to_ip);
+}
+
+
+/****************************************************************************
+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;
+  BOOL success = False;
+  
+  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?? */
+  /* For now - remove if the names match and the group bit matches. */
+  if (n && (n->source != SELF) && (NAME_GROUP(n->ip_flgs[0].nb_flags) == NAME_GROUP(nb_flags)))
+    {
+      success = True;
+     
+      DEBUG(5, ("reply_name_release: Removing name %s on subnet %s\n",
+                namestr(&nmb->question.question_name), inet_ntoa(d->bcast_ip)));
+      remove_name(d,n);
+      n = NULL;
+    }
+  
+  if (bcast) return;
+  
+  /* Send a NAME RELEASE RESPONSE (pos/neg) see rfc1002.txt 4.2.10-11 */
+  send_name_response(p->fd,p->ip, nmb->header.name_trn_id, NMB_REL,
+                    success, nmb->header.nm_flags.recursion_desired,
+                    &nmb->question.question_name, nb_flags, 0, ip);
+}
+
 
 /****************************************************************************
 reply to a reg request
@@ -156,10 +178,6 @@ void reply_name_reg(struct packet_struct *p)
 
   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;
@@ -167,76 +185,45 @@ void reply_name_reg(struct packet_struct *p)
   putip((char *)&from_ip,&nmb->additional->rdata[2]);
   ip = from_ip;
   
-  DEBUG(3,("Name registration for name %s at %s\n",
+  DEBUG(3,("Name registration for name %s at %s - ",
                   namestr(question),inet_ntoa(ip)));
   
   if (group)
     {
       /* apparently we should return 255.255.255.255 for group queries
         (email from MS) */
-      ip = ipgrp;
+      ip = *interpret_addr2("255.255.255.255");
     }
   
   if (!(d = find_req_subnet(p->ip, bcast)))
   {
-    DEBUG(3,("response packet: bcast %s not known\n",
+    DEBUG(3,("reply_name_reg: subnet %s not known\n",
                                inet_ntoa(p->ip)));
     return;
   }
 
   if (bcast)
-       search &= FIND_LOCAL;
+       search |= FIND_LOCAL;
   else
-       search &= FIND_WINS;
+       search |= FIND_WINS;
 
   /* see if the name already exists */
   n = find_name_search(&d, question, search, from_ip);
   
   if (n)
   {
+    DEBUG(3,("found\n"));
     if (!group) /* unique names */
        {
-         if (n->source == SELF || NAME_GROUP(n->nb_flags))
+         if (n->source == SELF || NAME_GROUP(n->ip_flgs[0].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))
+         else if(!ip_equal(ip, n->ip_flgs[0].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
@@ -246,18 +233,14 @@ void reply_name_reg(struct packet_struct *p)
               */
 
           secured_redirect = True;
-             recurse = False;
 
              reply_name = &n->name;
-
-#endif /* 0 */
-
          }
          else
          {
-             n->ip = ip;
+             n->ip_flgs[0].ip = ip;
              n->death_time = ttl?p->timestamp+ttl*3:0;
-             DEBUG(3,("%s owner: %s\n",namestr(&n->name),inet_ntoa(n->ip)));
+             DEBUG(3,("%s owner: %s\n",namestr(&n->name),inet_ntoa(n->ip_flgs[0].ip)));
          }
        }
     else
@@ -279,6 +262,7 @@ void reply_name_reg(struct packet_struct *p)
   }
   else
   {
+      DEBUG(3,("not found\n"));
       /* 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);
@@ -296,7 +280,7 @@ void reply_name_reg(struct packet_struct *p)
   {
     char rdata[2];
 
-    /* XXXX luke is confused. RSVAL or SSVAL? assume NMB byte ordering */
+    /* XXXX i am 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 
@@ -306,16 +290,17 @@ void reply_name_reg(struct packet_struct *p)
 
     /* send WAIT ACKNOWLEDGEMENT see rfc1002.txt 4.2.16 */
     reply_netbios_packet(p,nmb->header.name_trn_id,
-                      0,NMB_WAIT_ACK,False,
+                      0,NMB_WAIT_ACK,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);
+                        NAME_REGISTER_CHALLENGE,
+                        reply_name->name,reply_name->name_type,
+                        nb_flags,0,0,NULL,NULL,
+                        False, False, n->ip_flgs[0].ip, p->ip);
   }
   else
   {
@@ -323,30 +308,56 @@ void reply_name_reg(struct packet_struct *p)
        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);
+       send_name_response(p->fd,p->ip, nmb->header.name_trn_id, NMB_REG,
+                       success, nmb->header.nm_flags.recursion_desired,
+                       reply_name, nb_flags, ttl, ip);
   }
 }
 
+/* this is used to sort names for a name status into a sensible order
+   we put our own names first, then in alphabetical order */
+static int status_compare(char *n1,char *n2)
+{
+  extern pstring myname;
+  int l1,l2,l3;
+
+  /* its a bit tricky because the names are space padded */
+  for (l1=0;l1<15 && n1[l1] && n1[l1] != ' ';l1++) ;
+  for (l2=0;l2<15 && n2[l2] && n2[l2] != ' ';l2++) ;
+  l3 = strlen(myname);
+
+  if ((l1==l3) && strncmp(n1,myname,l3) == 0 && 
+      (l2!=l3 || strncmp(n2,myname,l3) != 0))
+    return -1;
+
+  if ((l2==l3) && strncmp(n2,myname,l3) == 0 && 
+      (l1!=l3 || strncmp(n1,myname,l3) != 0))
+    return 1;
+
+  return memcmp(n1,n2,18);
+}
+
 
 /****************************************************************************
-reply to a name status query
-****************************************************************************/
+  reply to a name status query
+
+  combine the list of the local interface on which the query was made with
+  the names registered via wins.
+  ****************************************************************************/
 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;
+  char *countptr, *buf, *bufend, *buf0;
+  int names_added,i;
   struct name_record *n;
   struct subnet_record *d = NULL;
+  int search = FIND_SELF | FIND_WINS | FIND_LOCAL;
 
-  BOOL bcast = nmb->header.nm_flags.bcast;
-  
-  if (!(d = find_req_subnet(p->ip, bcast)))
+  /* NOTE: we always treat a name status lookup as a bcast */ 
+  if (!(d = find_req_subnet(p->ip, True)))
   {
     DEBUG(3,("Name status req: bcast %s not known\n",
                        inet_ntoa(p->ip)));
@@ -354,11 +365,11 @@ void reply_name_status(struct packet_struct *p)
   }
 
   DEBUG(3,("Name status for name %s %s\n",
-          namestr(&nmb->question.question_name), inet_ntoa(p->ip)));
-  
+          namestr(&nmb->question.question_name), 
+          inet_ntoa(p->ip)));
+
   n = find_name_search(&d, &nmb->question.question_name,
-                               FIND_SELF|FIND_LOCAL,
-                               p->ip);
+                               search, p->ip);
   
   if (!n) return;
   
@@ -366,40 +377,74 @@ void reply_name_status(struct packet_struct *p)
   bufend = &rdata[MAX_DGRAM_SIZE] - 18;
   countptr = buf = rdata;
   buf += 1;
-  
+  buf0 = buf;
+
   names_added = 0;
-  
-  for (n = d->namelist ; n && buf < bufend; n = n->next) 
+
+  n = d->namelist;
+
+  while (buf < bufend) 
+  {
+    if (n->source == SELF)
     {
       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 */
+      /* 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 */
+      if (!strequal(n->name.name,"*") &&
+         !strequal(n->name.name,"__SAMBA__") &&
+         (name_type < 0x1b || name_type >= 0x20 || 
+          ques_type < 0x1b || ques_type >= 0x20 ||
+          strequal(qname, n->name.name)))
+      {
+        /* start with first bit of putting info in buffer: the name */
+        bzero(buf,18);
+           sprintf(buf,"%-15.15s",n->name.name);
+        strupper(buf);
+        
+        /* put name type and netbios flags in buffer */
+        buf[15] = name_type;
+        buf[16]  = n->ip_flgs[0].nb_flags;
+        
+        buf += 18;
       
-      buf[15] = name_type;
-      buf[16]  = n->nb_flags;
-      
-      buf += 18;
-      
-      names_added++;
+        names_added++;
+      }
     }
+
+    /* remove duplicate names */
+    qsort(buf0,names_added,18,QSORT_CAST status_compare);
+
+    for (i=1;i<names_added;i++) {
+      if (memcmp(buf0 + 18*i,buf0 + 18*(i-1),16) == 0) {
+       names_added--;
+       if (names_added == i) break;
+       memmove(buf0 + 18*i,buf0 + 18*(i+1),18*(names_added-i));
+       i--;
+      }
+    }
+
+    buf = buf0 + 18*names_added;
+
+    n = n->next;
+
+    if (!n)
+    {
+      /* end of this name list: add wins names too? */
+      struct subnet_record *w_d;
+
+      if (!(w_d = wins_subnet)) break;
+
+      if (w_d != d)
+      {
+        d = w_d;
+        n = d->namelist; /* start on the wins name list */
+      }
+       }
+       if (!n) break;
+  }
   
   SCVAL(countptr,0,names_added);
   
@@ -411,18 +456,14 @@ void reply_name_status(struct packet_struct *p)
     SIVAL(buf,24,num_good_receives);
   }
   
-  SIVAL(buf,46,0xFFB8E5); /* undocumented - used by NT */
-  
-  buf += 64;
+  buf += 46;
   
   /* Send a POSITIVE NAME STATUS RESPONSE */
   reply_netbios_packet(p,nmb->header.name_trn_id,
-                          0,0,True,
+                          0,NMB_STATUS,0,True,
                       &nmb->question.question_name,
-                      nmb->question.question_type,
-                      nmb->question.question_class,
-                      0,
-                      rdata,PTR_DIFF(buf,rdata));
+                      0x21, 0x01,
+                      0, rdata,PTR_DIFF(buf,rdata));
 }
 
 
@@ -463,28 +504,34 @@ void reply_name_query(struct packet_struct *p)
   char rdata[6];
   struct subnet_record *d = NULL;
   BOOL success = True;
-  struct name_record *n;
+  struct name_record *n = NULL;
 
   /* directed queries are for WINS server: broadcasts are local SELF queries.
-     the exception is PDC names.  */
+     the exception is Domain Master names.  */
 
-  int search = bcast ? FIND_LOCAL | FIND_SELF : FIND_WINS;
-  
-  if (name_type == 0x1b)
+  int search = bcast ? FIND_LOCAL | FIND_WINS: FIND_WINS;
+
+  if (search & FIND_LOCAL)
   {
-    /* 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;
+    }
   }
-
-  if (!(d = find_req_subnet(p->ip, bcast)))
+  else
   {
-    DEBUG(3,("name query: bcast %s not known\n",
-                                 inet_ntoa(p->ip)));
-    success = False;
+    if (!(d = wins_subnet))
+    {
+      DEBUG(3,("name query: wins search %s not known\n",
+                                   inet_ntoa(p->ip)));
+      success = False;
+    }
   }
 
-  DEBUG(3,("Name query "));
+  DEBUG(3,("Name query from %s for name %s<0x%x>\n", 
+                  inet_ntoa(p->ip), question->name, question->name_type));
   
   if (search == 0)
   {
@@ -492,40 +539,81 @@ void reply_name_query(struct packet_struct *p)
     success = False;
   }
 
-  if (success && (n = search_for_name(&d,question,p->ip,p->timestamp, search)))
+  if (!bcast && (name_type == 0x1d) && lp_wins_support())
   {
-      /* don't respond to broadcast queries unless the query is for
-         a name we own or it is for a Primary Domain Controller name */
+    /* see WINS manager HELP - 'How WINS Handles Special Names' */
+    /* a WINS query (unicasted) for a 0x1d name must always return False */
+    success = False;
+  }
 
-      if (bcast && n->source != SELF && name_type != 0x1b) {
-           if (!lp_wins_proxy() || same_net(p->ip,n->ip,*iface_nmask(p->ip))) {
+  if (success)
+  {
+    /* look up the name in the cache */
+    n = find_name_search(&d, question, search, p->ip);
+
+    /* it is a name that already failed DNS lookup or it's expired */
+    if (n && (n->source == DNSFAIL ||
+              (n->death_time && n->death_time < p->timestamp)))
+    {
+      success = False;
+    }
+   
+    /* do we want to do dns lookups? */
+    /* XXXX this DELAYS nmbd while it does a search. not a good idea
+       but there's no pleasant alternative. phil@hands.com suggested
+       making the name a full DNS name, which would succeed / fail
+       much quicker.
+     */
+    if (success && !n && (lp_wins_proxy() || !bcast))
+    {
+      n = dns_name_search(question, p->timestamp);
+    }
+  }
+
+  if (!n) success = False;
+  
+  if (success)
+  {
+      if (bcast && n->source != SELF && name_type != 0x1b)
+      {
+        /* 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 (!lp_wins_proxy() || 
+            same_net(p->ip,n->ip_flgs[0].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 */
+      /* name is directed query, or it's self, or it's a Domain Master 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;
+      ttl = n->death_time ? n->death_time - p->timestamp : GET_TTL(0);
+      retip = n->ip_flgs[0].ip;
+      nb_flags = n->ip_flgs[0].nb_flags;
   }
-  else
-  {
-      if (bcast) return; /* never reply negative response to bcasts */
-      success = False;
-  }
-  
+
+  if (!success && bcast) return; /* never reply negative response to bcasts */
+
   /* if the IP is 0 then substitute my IP */
   if (zero_ip(retip)) retip = *iface_ip(p->ip);
 
+  /* SPECIAL CASE... If we are a WINS server and the request is explicitly
+     *to* the WINS server and the name type is WORKGROUP<0x1e> we should 
+     respond with the local broadcast address 255.255.255.255.
+   */
+  if(!bcast && (name_type == 0x1e) && lp_wins_support())
+    retip = *interpret_addr2("255.255.255.255");
+
   if (success)
   {
       rcode = 0;
@@ -545,12 +633,9 @@ void reply_name_query(struct packet_struct *p)
   }
   
   reply_netbios_packet(p,nmb->header.name_trn_id,
-                          rcode,0,True,
+                          rcode,NMB_QUERY,0,True,
                       &nmb->question.question_name,
-                      nmb->question.question_type,
-                      nmb->question.question_class,
+               0x20, 0x01,
                       ttl,
                       rdata, success ? 6 : 0);
 }
-
-