first pass at updating head branch to be to be the same as the SAMBA_2_0 branch
[kai/samba.git] / source3 / nmbd / nmbd_winsserver.c
index acab81592614b59953d3d7f7e9e0ca59f487ec7f..45e93351674289924ea277c96f72dda5b0c83e9c 100644 (file)
 #include "includes.h"
 
 #define WINS_LIST "wins.dat"
+#define WINS_VERSION 1
 
 extern int DEBUGLEVEL;
 extern struct in_addr ipzero;
 
+
+/****************************************************************************
+possibly call the WINS hook external program when a WINS change is made
+*****************************************************************************/
+static void wins_hook(char *operation, struct name_record *namerec, int ttl)
+{
+       pstring command;
+       char *cmd = lp_wins_hook();
+       char *p;
+       int i;
+
+       if (!cmd || !*cmd) return;
+
+       for (p=namerec->name.name; *p; p++) {
+               if (!(isalnum((int)*p) || strchr("._-",*p))) {
+                       DEBUG(3,("not calling wins hook for invalid name %s\n", nmb_namestr(&namerec->name)));
+                       return;
+               }
+       }
+       
+       p = command;
+       p += slprintf(p, sizeof(command), "%s %s %s %02x %d", 
+                     cmd,
+                     operation, 
+                     namerec->name.name,
+                     namerec->name.name_type,
+                     ttl);
+
+       for (i=0;i<namerec->data.num_ips;i++) {
+               p += slprintf(p, sizeof(command) - (p-command), " %s", inet_ntoa(namerec->data.ip[i]));
+       }
+
+       DEBUG(3,("calling wins hook for %s\n", nmb_namestr(&namerec->name)));
+       smbrun(command, NULL, False);
+}
+
+
+/****************************************************************************
+hash our interfaces and netbios names settings
+*****************************************************************************/
+static unsigned wins_hash(void)
+{
+       int i;
+       unsigned ret = iface_hash();
+       extern char **my_netbios_names;
+
+       for (i=0;my_netbios_names[i];i++)
+               ret ^= str_checksum(my_netbios_names[i]);
+       
+       ret ^= str_checksum(lp_workgroup());
+
+       return ret;
+}
+       
+
 /****************************************************************************
 Determine if this packet should be allocated to the WINS server.
 *****************************************************************************/
@@ -126,17 +182,12 @@ BOOL initialise_wins(void)
 
   add_samba_names_to_subnet(wins_server_subnet);
 
-#ifndef SYNC_DNS
-  /* Setup the async dns. */
-  start_async_dns();
-#endif
-
   pstrcpy(fname,lp_lockdir());
   trim_string(fname,NULL,"/");
   pstrcat(fname,"/");
   pstrcat(fname,WINS_LIST);
 
-  if((fp = fopen(fname,"r")) == NULL)
+  if((fp = sys_fopen(fname,"r")) == NULL)
   {
     DEBUG(2,("initialise_wins: Can't open wins database file %s. Error was %s\n",
            fname, strerror(errno) ));
@@ -152,12 +203,13 @@ BOOL initialise_wins(void)
     int type = 0;
     int nb_flags;
     int ttl;
-    enum name_source source;
     char *ptr;
     char *p;
     BOOL got_token;
     BOOL was_ip;
     int i;
+    unsigned hash;
+    int version;
 
     /* Read a line from the wins.dat file. Strips whitespace
        from the beginning and end of the line.
@@ -168,6 +220,17 @@ BOOL initialise_wins(void)
     if (*line == '#')
       continue;
 
+    if (strncmp(line,"VERSION ", 8) == 0) {
+           if (sscanf(line,"VERSION %d %u", &version, &hash) != 2 ||
+               version != WINS_VERSION ||
+               hash != wins_hash()) {
+                   DEBUG(0,("Discarding invalid wins.dat file [%s]\n",line));
+                   fclose(fp);
+                   return True;
+           }
+           continue;
+    }
+
     ptr = line;
 
     /* 
@@ -177,13 +240,13 @@ BOOL initialise_wins(void)
      * time to actually parse them into the ip_list array.
      */
 
-    if (!next_token(&ptr,name_str,NULL)) 
+    if (!next_token(&ptr,name_str,NULL,sizeof(name_str))) 
     {
       DEBUG(0,("initialise_wins: Failed to parse name when parsing line %s\n", line ));
       continue;
     }
 
-    if (!next_token(&ptr,ttl_str,NULL))
+    if (!next_token(&ptr,ttl_str,NULL,sizeof(ttl_str)))
     {
       DEBUG(0,("initialise_wins: Failed to parse time to live when parsing line %s\n", line ));
       continue;
@@ -195,7 +258,7 @@ BOOL initialise_wins(void)
     num_ips = 0;
     do
     {
-      got_token = next_token(&ptr,ip_str,NULL);
+      got_token = next_token(&ptr,ip_str,NULL,sizeof(ip_str));
       was_ip = False;
 
       if(got_token && strchr(ip_str, '.'))
@@ -226,16 +289,14 @@ BOOL initialise_wins(void)
  
     /* Reset and re-parse the line. */
     ptr = line;
-    next_token(&ptr,name_str,NULL); 
-    next_token(&ptr,ttl_str,NULL);
+    next_token(&ptr,name_str,NULL,sizeof(name_str)); 
+    next_token(&ptr,ttl_str,NULL,sizeof(ttl_str));
     for(i = 0; i < num_ips; i++)
     {
-      next_token(&ptr, ip_str, NULL);
+      next_token(&ptr, ip_str, NULL, sizeof(ip_str));
       ip_list[i] = *interpret_addr2(ip_str);
-      if (ip_equal(ip_list[i], ipzero)) 
-         source = SELF_NAME;
     }
-    next_token(&ptr,nb_flags_str,NULL);
+    next_token(&ptr,nb_flags_str,NULL, sizeof(nb_flags_str));
 
     /* 
      * Deal with SELF or REGISTER name encoding. Default is REGISTER
@@ -268,16 +329,14 @@ BOOL initialise_wins(void)
     /* add all entries that have 60 seconds or more to live */
     if ((ttl - 60) > time_now || ttl == PERMANENT_TTL)
     {
-      struct name_record *namerec;
-
       if(ttl != PERMANENT_TTL)
         ttl -= time_now;
     
-      DEBUG(4, ("initialise_wins: add name: %s#%02x ttl = %d first IP %s flags = %2x\n",
+      DEBUG( 4, ("initialise_wins: add name: %s#%02x ttl = %d first IP %s flags = %2x\n",
            name, type, ttl, inet_ntoa(ip_list[0]), nb_flags));
 
-      namerec = add_name_to_subnet(wins_server_subnet, name, type, nb_flags, 
-                                   ttl, REGISTER_NAME, num_ips, ip_list);
+      (void)add_name_to_subnet( wins_server_subnet, name, type, nb_flags, 
+                                    ttl, REGISTER_NAME, num_ips, ip_list );
 
     }
     else
@@ -373,12 +432,12 @@ void wins_process_name_refresh_request(struct subnet_record *subrec,
 
     DEBUG(0,("wins_process_name_refresh_request: broadcast name refresh request \
 received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n",
-          namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+          nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
     return;
   }
 
   DEBUG(3,("wins_process_name_refresh_request: Name refresh for name %s \
-IP %s\n", namestr(question), inet_ntoa(from_ip) ));
+IP %s\n", nmb_namestr(question), inet_ntoa(from_ip) ));
 
   /* 
    * See if the name already exists.
@@ -388,14 +447,15 @@ IP %s\n", namestr(question), inet_ntoa(from_ip) ));
 
   /*
    * If this is a refresh request and the name doesn't exist then
-   * fail it.
+   * treat it like a registration request. This allows us to recover 
+   * from errors (tridge)
    */
 
   if(namerec == NULL)
   {
     DEBUG(3,("wins_process_name_refresh_request: Name refresh for name %s and \
-the name does not exist.\n", namestr(question) ));
-    send_wins_name_registration_response(NAM_ERR, 0, p);
+the name does not exist. Treating as registration.\n", nmb_namestr(question) ));
+    wins_process_name_registration_request(subrec,p);
     return;
   }
 
@@ -407,7 +467,7 @@ the name does not exist.\n", namestr(question) ));
   if((namerec != NULL) && ((group && !NAME_GROUP(namerec)) || (!group && NAME_GROUP(namerec))) )
   {
     DEBUG(3,("wins_process_name_refresh_request: Name %s group bit = %s \
-does not match group bit in WINS for this name.\n", namestr(question), group ? "True" : "False" ));
+does not match group bit in WINS for this name.\n", nmb_namestr(question), group ? "True" : "False" ));
     send_wins_name_registration_response(RFS_ERR, 0, p);
     return;
   }
@@ -426,6 +486,7 @@ does not match group bit in WINS for this name.\n", namestr(question), group ? "
      */
     update_name_ttl(namerec, ttl);
     send_wins_name_registration_response(0, ttl, p);
+    wins_hook("refresh", namerec, ttl);
     return;
   }
   else if(group)
@@ -453,7 +514,7 @@ does not match group bit in WINS for this name.\n", namestr(question), group ? "
      */
 
     DEBUG(3,("wins_process_name_refresh_request: Name refresh for name %s with IP %s and \
-is IP is not known to the name.\n", namestr(question), inet_ntoa(from_ip) ));
+is IP is not known to the name.\n", nmb_namestr(question), inet_ntoa(from_ip) ));
     send_wins_name_registration_response(RFS_ERR, 0, p);
     return;
   }
@@ -480,7 +541,7 @@ static void wins_register_query_success(struct subnet_record *subrec,
   memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *));
 
   DEBUG(3,("wins_register_query_success: Original client at IP %s still wants the \
-name %s. Rejecting registration request.\n", inet_ntoa(ip), namestr(question_name) ));
+name %s. Rejecting registration request.\n", inet_ntoa(ip), nmb_namestr(question_name) ));
 
   send_wins_name_registration_response(RFS_ERR, 0, orig_reg_packet);
 
@@ -505,18 +566,10 @@ static void wins_register_query_fail(struct subnet_record *subrec,
 {
   struct userdata_struct *userdata = rrec->userdata;
   struct packet_struct *orig_reg_packet;
-  struct nmb_packet *nmb;
   struct name_record *namerec = NULL;
-  uint16 nb_flags;
-  BOOL group;
 
   memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *));
 
-  nmb = &orig_reg_packet->packet.nmb;
-
-  nb_flags = get_nb_flags(nmb->additional->rdata);
-  group = (nb_flags & NB_GROUP) ? True : False;
-
   /*
    * We want to just add the name, as we now know the original owner
    * didn't want it. But we can't just do that as an arbitary
@@ -529,8 +582,9 @@ static void wins_register_query_fail(struct subnet_record *subrec,
 
   namerec = find_name_on_subnet(subrec, question_name, FIND_ANY_NAME);
 
-  if((namerec != NULL) && (namerec->source == REGISTER_NAME) && 
-            ip_equal(rrec->packet->ip, *namerec->ip) )
+  if( (namerec != NULL)
+   && (namerec->data.source == REGISTER_NAME)
+   && ip_equal(rrec->packet->ip, *namerec->data.ip) )
   {
     remove_name_from_namelist( subrec, namerec);
     namerec = NULL;
@@ -540,7 +594,7 @@ static void wins_register_query_fail(struct subnet_record *subrec,
     wins_process_name_registration_request(subrec, orig_reg_packet);
   else
     DEBUG(2,("wins_register_query_fail: The state of the WINS database changed between \
-querying for name %s in order to replace it and this reply.\n", namestr(question_name) ));
+querying for name %s in order to replace it and this reply.\n", nmb_namestr(question_name) ));
 
   orig_reg_packet->locked = False;
   free_packet(orig_reg_packet);
@@ -613,7 +667,7 @@ void wins_process_name_registration_request(struct subnet_record *subrec,
   int ttl = get_ttl_from_packet(nmb);
   struct name_record *namerec = NULL;
   struct in_addr from_ip;
-  BOOL registering_group_name = (nb_flags & NB_GROUP) ? True : False;;
+  BOOL registering_group_name = (nb_flags & NB_GROUP) ? True : False;
 
   putip((char *)&from_ip,&nmb->additional->rdata[2]);
 
@@ -627,12 +681,12 @@ void wins_process_name_registration_request(struct subnet_record *subrec,
 
     DEBUG(0,("wins_process_name_registration_request: broadcast name registration request \
 received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n",
-          namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+          nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
     return;
   }
 
   DEBUG(3,("wins_process_name_registration_request: %s name registration for name %s \
-IP %s\n", registering_group_name ? "Group" : "Unique", namestr(question), inet_ntoa(from_ip) ));
+IP %s\n", registering_group_name ? "Group" : "Unique", nmb_namestr(question), inet_ntoa(from_ip) ));
 
   /*
    * See if the name already exists.
@@ -646,11 +700,13 @@ IP %s\n", registering_group_name ? "Group" : "Unique", namestr(question), inet_n
    * name.
    */
 
-  if((namerec != NULL) && ((namerec->source == DNS_NAME) || (namerec->source == DNSFAIL_NAME)))
+  if( (namerec != NULL)
+   && ( (namerec->data.source == DNS_NAME)
+     || (namerec->data.source == DNSFAIL_NAME) ) )
   {
-    DEBUG(5,("wins_process_name_registration_request: Name (%s) in WINS was a dns lookup \
-- removing it.\n", namestr(question) ));
-    remove_name_from_namelist( subrec, namerec);
+    DEBUG(5,("wins_process_name_registration_request: Name (%s) in WINS was \
+a dns lookup - removing it.\n", nmb_namestr(question) ));
+    remove_name_from_namelist( subrec, namerec );
     namerec = NULL;
   }
 
@@ -659,10 +715,11 @@ IP %s\n", registering_group_name ? "Group" : "Unique", namestr(question), inet_n
    * (ie. Don't allow any static names to be overwritten.
    */
 
-  if((namerec != NULL) && (namerec->source != REGISTER_NAME))
+  if((namerec != NULL) && (namerec->data.source != REGISTER_NAME))
   {
-    DEBUG(3,("wins_process_name_registration_request: Attempt to register name %s. Name \
-already exists in WINS with source type %d.\n", namestr(question), namerec->source ));
+    DEBUG( 3, ( "wins_process_name_registration_request: Attempt \
+to register name %s. Name already exists in WINS with source type %d.\n",
+                nmb_namestr(question), namerec->data.source ));
     send_wins_name_registration_response(RFS_ERR, 0, p);
     return;
   }
@@ -689,7 +746,7 @@ already exists in WINS with source type %d.\n", namestr(question), namerec->sour
   if(!registering_group_name && (question->name_type == 0x1d))
   {
     DEBUG(3,("wins_process_name_registration_request: Ignoring request \
-to register name %s from IP %s.", namestr(question), inet_ntoa(p->ip) ));
+to register name %s from IP %s.\n", nmb_namestr(question), inet_ntoa(p->ip) ));
     send_wins_name_registration_response(0, ttl, p);
     return;
   }
@@ -708,7 +765,7 @@ to register name %s from IP %s.", namestr(question), inet_ntoa(p->ip) ));
        */
 
       DEBUG(3,("wins_process_name_registration_request: Adding IP %s to group name %s.\n",
-            inet_ntoa(from_ip), namestr(question) ));
+            inet_ntoa(from_ip), nmb_namestr(question) ));
       /* 
        * Check the ip address is not already in the group.
        */
@@ -726,7 +783,7 @@ to register name %s from IP %s.", namestr(question), inet_ntoa(p->ip) ));
        */
 
       DEBUG(3,("wins_process_name_registration_request: Attempt to register name %s. Name \
-already exists in WINS as a GROUP name.\n", namestr(question) ));
+already exists in WINS as a GROUP name.\n", nmb_namestr(question) ));
       send_wins_name_registration_response(RFS_ERR, 0, p);
       return;
     } 
@@ -748,7 +805,7 @@ already exists in WINS as a GROUP name.\n", namestr(question) ));
     if(!ismyip(from_ip))
     {
       DEBUG(3,("wins_process_name_registration_request: Attempt to register name %s. Name \
-is one of our (WINS server) names. Denying registration.\n", namestr(question) ));
+is one of our (WINS server) names. Denying registration.\n", nmb_namestr(question) ));
       send_wins_name_registration_response(RFS_ERR, 0, p);
       return;
     }
@@ -759,6 +816,7 @@ is one of our (WINS server) names. Denying registration.\n", namestr(question) )
        */
       update_name_ttl(namerec, ttl);
       send_wins_name_registration_response(0, ttl, p);
+      wins_hook("refresh", namerec, ttl);
       return;
     }
   }
@@ -768,11 +826,14 @@ is one of our (WINS server) names. Denying registration.\n", namestr(question) )
    * is the same as the the (single) already registered IP then just update the ttl.
    */
 
-  if(!registering_group_name && (namerec != NULL) && (namerec->num_ips == 1) && 
-           ip_equal(namerec->ip[0], from_ip))
+  if( !registering_group_name
+   && (namerec != NULL)
+   && (namerec->data.num_ips == 1)
+   && ip_equal( namerec->data.ip[0], from_ip ) )
   {
-    update_name_ttl(namerec, ttl);
-    send_wins_name_registration_response(0, ttl, p);
+    update_name_ttl( namerec, ttl );
+    send_wins_name_registration_response( 0, ttl, p );
+    wins_hook("refresh", namerec, ttl);
     return;
   }
 
@@ -781,7 +842,7 @@ is one of our (WINS server) names. Denying registration.\n", namestr(question) )
    * to see if they still claim to have the name.
    */
 
-  if(namerec != NULL)
+  if( namerec != NULL )
   {
     char ud[sizeof(struct userdata_struct) + sizeof(struct packet_struct *)];
     struct userdata_struct *userdata = (struct userdata_struct *)ud;
@@ -815,10 +876,12 @@ is one of our (WINS server) names. Denying registration.\n", namestr(question) )
      * code. JRA.
      */
 
-    query_name_from_wins_server( *namerec->ip, question->name, question->name_type, 
-                wins_register_query_success,
-                wins_register_query_fail,
-                userdata);
+    query_name_from_wins_server( *namerec->data.ip,
+                                  question->name,
+                                  question->name_type, 
+                                  wins_register_query_success,
+                                  wins_register_query_fail,
+                                  userdata );
     return;
   }
 
@@ -826,8 +889,11 @@ is one of our (WINS server) names. Denying registration.\n", namestr(question) )
    * Name did not exist - add it.
    */
 
-  add_name_to_subnet(subrec, question->name, question->name_type,
-                     nb_flags, ttl, REGISTER_NAME, 1, &from_ip);
+  (void)add_name_to_subnet( subrec, question->name, question->name_type,
+                            nb_flags, ttl, REGISTER_NAME, 1, &from_ip );
+  if ((namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME))) {
+         wins_hook("add", namerec, ttl);
+  }
 
   send_wins_name_registration_response(0, ttl, p);
 }
@@ -870,11 +936,15 @@ static void wins_multihomed_register_query_success(struct subnet_record *subrec,
 
   namerec = find_name_on_subnet(subrec, question_name, FIND_ANY_NAME);
 
-  if( (namerec == NULL) || (namerec->source != REGISTER_NAME) )
+  if( (namerec == NULL) || (namerec->data.source != REGISTER_NAME) )
   {
     DEBUG(3,("wins_multihomed_register_query_success: name %s is not in the correct state to add \
-a subsequent IP addess.\n", namestr(question_name) ));
+a subsequent IP addess.\n", nmb_namestr(question_name) ));
     send_wins_name_registration_response(RFS_ERR, 0, orig_reg_packet);
+
+    orig_reg_packet->locked = False;
+    free_packet(orig_reg_packet);
+
     return;
   }
 
@@ -882,7 +952,10 @@ a subsequent IP addess.\n", namestr(question_name) ));
     add_ip_to_name_record(namerec, from_ip);
   update_name_ttl(namerec, ttl);
   send_wins_name_registration_response(0, ttl, orig_reg_packet);
+  wins_hook("add", namerec, ttl);
 
+  orig_reg_packet->locked = False;
+  free_packet(orig_reg_packet);
 }
 
 /***********************************************************************
@@ -904,8 +977,11 @@ static void wins_multihomed_register_query_fail(struct subnet_record *subrec,
   memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *));
 
   DEBUG(3,("wins_multihomed_register_query_fail: Registering machine at IP %s failed to answer \
-query successfully for name %s.\n", inet_ntoa(orig_reg_packet->ip), namestr(question_name) ));
+query successfully for name %s.\n", inet_ntoa(orig_reg_packet->ip), nmb_namestr(question_name) ));
   send_wins_name_registration_response(RFS_ERR, 0, orig_reg_packet);
+
+  orig_reg_packet->locked = False;
+  free_packet(orig_reg_packet);
   return;
 }
 
@@ -938,7 +1014,7 @@ void wins_process_multihomed_name_registration_request( struct subnet_record *su
 
     DEBUG(0,("wins_process_multihomed_name_registration_request: broadcast name registration request \
 received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n",
-          namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+          nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
     return;
   }
 
@@ -950,12 +1026,12 @@ received for name %s from IP %s on subnet %s. Error - should not be sent to WINS
   {
     DEBUG(0,("wins_process_multihomed_name_registration_request: group name registration request \
 received for name %s from IP %s on subnet %s. Errror - group names should not be multihomed.\n",
-          namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+          nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
     return;
   }
 
   DEBUG(3,("wins_process_multihomed_name_registration_request: name registration for name %s \
-IP %s\n", namestr(question), inet_ntoa(from_ip) ));
+IP %s\n", nmb_namestr(question), inet_ntoa(from_ip) ));
 
   /*
    * Deal with policy regarding 0x1d names.
@@ -964,7 +1040,7 @@ IP %s\n", namestr(question), inet_ntoa(from_ip) ));
   if(question->name_type == 0x1d)
   {
     DEBUG(3,("wins_process_multihomed_name_registration_request: Ignoring request \
-to register name %s from IP %s.", namestr(question), inet_ntoa(p->ip) ));
+to register name %s from IP %s.", nmb_namestr(question), inet_ntoa(p->ip) ));
     send_wins_name_registration_response(0, ttl, p);  
     return;
   }
@@ -981,10 +1057,12 @@ to register name %s from IP %s.", namestr(question), inet_ntoa(p->ip) ));
    * name.
    */
 
-  if((namerec != NULL) && ((namerec->source == DNS_NAME) || (namerec->source == DNSFAIL_NAME)))
+  if( (namerec != NULL)
+   && ( (namerec->data.source == DNS_NAME)
+     || (namerec->data.source == DNSFAIL_NAME) ) )
   {
     DEBUG(5,("wins_process_multihomed_name_registration_request: Name (%s) in WINS was a dns lookup \
-- removing it.\n", namestr(question) ));
+- removing it.\n", nmb_namestr(question) ));
     remove_name_from_namelist( subrec, namerec);
     namerec = NULL;
   }
@@ -994,10 +1072,11 @@ to register name %s from IP %s.", namestr(question), inet_ntoa(p->ip) ));
    * (ie. Don't allow any static names to be overwritten.
    */
 
-  if((namerec != NULL) && (namerec->source != REGISTER_NAME))
+  if( (namerec != NULL) && (namerec->data.source != REGISTER_NAME) )
   {
-    DEBUG(3,("wins_process_multihomed_name_registration_request: Attempt to register name %s. Name \
-already exists in WINS with source type %d.\n", namestr(question), namerec->source ));
+    DEBUG( 3, ( "wins_process_multihomed_name_registration_request: Attempt \
+to register name %s. Name already exists in WINS with source type %d.\n",
+    nmb_namestr(question), namerec->data.source ));
     send_wins_name_registration_response(RFS_ERR, 0, p);
     return;
   }
@@ -1009,7 +1088,7 @@ already exists in WINS with source type %d.\n", namestr(question), namerec->sour
   if((namerec != NULL) && NAME_GROUP(namerec))
   {
     DEBUG(3,("wins_process_multihomed_name_registration_request: Attempt to register name %s. Name \
-already exists in WINS as a GROUP name.\n", namestr(question) ));
+already exists in WINS as a GROUP name.\n", nmb_namestr(question) ));
     send_wins_name_registration_response(RFS_ERR, 0, p);
     return;
   } 
@@ -1030,7 +1109,7 @@ already exists in WINS as a GROUP name.\n", namestr(question) ));
     if(!ismyip(from_ip))
     {
       DEBUG(3,("wins_process_multihomed_name_registration_request: Attempt to register name %s. Name \
-is one of our (WINS server) names. Denying registration.\n", namestr(question) ));
+is one of our (WINS server) names. Denying registration.\n", nmb_namestr(question) ));
       send_wins_name_registration_response(RFS_ERR, 0, p);
       return;
     }
@@ -1040,11 +1119,16 @@ is one of our (WINS server) names. Denying registration.\n", namestr(question) )
        * It's one of our names and one of our IP's. Ensure the IP is in the record and
        *  update the ttl.
        */
-      if(!find_ip_in_name_record(namerec, from_ip))
-        add_ip_to_name_record(namerec, from_ip);
-      update_name_ttl(namerec, ttl);
-      send_wins_name_registration_response(0, ttl, p);
-      return;
+           if(!find_ip_in_name_record(namerec, from_ip)) {
+                   add_ip_to_name_record(namerec, from_ip);
+                   wins_hook("add", namerec, ttl);
+           } else {
+                   wins_hook("refresh", namerec, ttl);
+           }
+
+           update_name_ttl(namerec, ttl);
+           send_wins_name_registration_response(0, ttl, p);
+           return;
     }
   }
 
@@ -1057,6 +1141,7 @@ is one of our (WINS server) names. Denying registration.\n", namestr(question) )
   {
     update_name_ttl(namerec, ttl);
     send_wins_name_registration_response(0, ttl, p);
+    wins_hook("refresh", namerec, ttl);
     return;
   }
 
@@ -1091,20 +1176,21 @@ is one of our (WINS server) names. Denying registration.\n", namestr(question) )
     userdata->userdata_len = sizeof(struct packet_struct *);
     memcpy(userdata->data, (char *)&p, sizeof(struct packet_struct *) );
 
-    /*
-     * As query_name uses the subnet broadcast address as the destination
-     * of the packet we temporarily change the subnet broadcast address to
-     * be the IP address of the requesting machine and send the packet. This
-     * is a *horrible* hack but the alternative is to add the destination
-     * address parameter to all query_name() calls. I hate this code :-).
+    /* 
+     * Use the new call to send a query directly to an IP address.
+     * This sends the query directly to the IP address, and ensures
+     * the recursion desired flag is not set (you were right Luke :-).
+     * This function should *only* be called from the WINS server
+     * code. JRA.
      */
 
-    subrec->bcast_ip = p->ip;
-    query_name( subrec, question->name, question->name_type,
-                wins_multihomed_register_query_success, 
-                wins_multihomed_register_query_fail,
-                userdata);
-    subrec->bcast_ip = ipzero;
+    query_name_from_wins_server( p->ip,
+                                 question->name,
+                                 question->name_type, 
+                                 wins_multihomed_register_query_success,
+                                 wins_multihomed_register_query_fail,
+                                 userdata );
+
     return;
   }
 
@@ -1112,8 +1198,12 @@ is one of our (WINS server) names. Denying registration.\n", namestr(question) )
    * Name did not exist - add it.
    */
 
-  add_name_to_subnet(subrec, question->name, question->name_type,
-                     nb_flags, ttl, REGISTER_NAME, 1, &from_ip);
+  (void)add_name_to_subnet( subrec, question->name, question->name_type,
+                            nb_flags, ttl, REGISTER_NAME, 1, &from_ip );
+
+  if ((namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME))) {
+         wins_hook("add", namerec, ttl);
+  }
 
   send_wins_name_registration_response(0, ttl, p);
 }
@@ -1136,10 +1226,12 @@ static void process_wins_dmb_query_request(struct subnet_record *subrec,
    */
 
   num_ips = 0;
-  for(namerec = subrec->namelist; namerec; namerec = namerec->next)
+  for( namerec = (struct name_record *)ubi_trFirst( subrec->namelist );
+       namerec;
+       namerec = (struct name_record *)ubi_trNext( namerec ) )
   {
-    if(namerec->name.name_type == 0x1b)
-      num_ips += namerec->num_ips;
+    if( namerec->name.name_type == 0x1b )
+      num_ips += namerec->data.num_ips;
   }
 
   if(num_ips == 0)
@@ -1164,15 +1256,17 @@ static void process_wins_dmb_query_request(struct subnet_record *subrec,
    */ 
 
   num_ips = 0;
-  for(namerec = subrec->namelist; namerec; namerec = namerec->next)
+  for( namerec = (struct name_record *)ubi_trFirst( subrec->namelist );
+       namerec;
+       namerec = (struct name_record *)ubi_trNext( namerec ) )
   {
     if(namerec->name.name_type == 0x1b)
     {
       int i;
-      for(i = 0; i < namerec->num_ips; i++)
+      for(i = 0; i < namerec->data.num_ips; i++)
       {
-        set_nb_flags(&prdata[num_ips * 6],namerec->nb_flags);
-        putip((char *)&prdata[(num_ips * 6) + 2], &namerec->ip[i]);
+        set_nb_flags(&prdata[num_ips * 6],namerec->data.nb_flags);
+        putip((char *)&prdata[(num_ips * 6) + 2], &namerec->data.ip[i]);
         num_ips++;
       }
     }
@@ -1204,69 +1298,39 @@ void send_wins_name_query_response(int rcode, struct packet_struct *p,
   char *prdata = rdata;
   int reply_data_len = 0;
   int ttl = 0;
-  int i = 0;
-  int j;
+  int i;
 
-  bzero(rdata,6);
+  memset(rdata,'\0',6);
 
   if(rcode == 0)
   {
-    int same_net_index = -1;
-
-    ttl = (namerec->death_time != PERMANENT_TTL) ?
-             namerec->death_time - p->timestamp : lp_max_wins_ttl();
+    ttl = (namerec->data.death_time != PERMANENT_TTL) ?
+             namerec->data.death_time - p->timestamp : lp_max_wins_ttl();
 
     /* Copy all known ip addresses into the return data. */
     /* Optimise for the common case of one IP address so
        we don't need a malloc. */
 
-    if(namerec->num_ips == 1 )
+    if( namerec->data.num_ips == 1 )
       prdata = rdata;
     else
     {
-      if((prdata = (char *)malloc( namerec->num_ips * 6 )) == NULL)
+      if((prdata = (char *)malloc( namerec->data.num_ips * 6 )) == NULL)
       {
         DEBUG(0,("send_wins_name_query_response: malloc fail !\n"));
         return;
       }
-
-      /* 
-       * Look over the known IP addresses and see if one of them
-       * is on the same (local) net as the requesting IP address. If so then
-       * put that IP address into the packet as the first IP.
-       * We can only do this for local nets as they're the only
-       * ones we know the netmask for.
-       */
-
-      i = 0;
-
-      if(is_local_net(p->ip))
-      {
-        struct in_addr *n_mask = iface_nmask(p->ip);
-
-        for( j = 0; j < namerec->num_ips; j++)
-        {
-          if(same_net( namerec->ip[j], p->ip, *n_mask))
-          {
-            set_nb_flags(&prdata[0],namerec->nb_flags);
-            putip((char *)&prdata[2], &namerec->ip[j]);
-            same_net_index = j;
-            i = 1;
-          }
-        }
-      }
     }
 
-    for(j = 0; j < namerec->num_ips; j++)
+    for(i = 0; i < namerec->data.num_ips; i++)
     {
-      if(j == same_net_index)
-        continue;
-      set_nb_flags(&prdata[i*6],namerec->nb_flags);
-      putip((char *)&prdata[2+(i*6)], &namerec->ip[j]);
-      i++;
+      set_nb_flags(&prdata[i*6],namerec->data.nb_flags);
+      putip((char *)&prdata[2+(i*6)], &namerec->data.ip[i]);
     }
-    reply_data_len = namerec->num_ips * 6;
 
+    sort_query_replies(prdata, i, p->ip);
+
+    reply_data_len = namerec->data.num_ips * 6;
   }
 
   reply_netbios_packet(p,                             /* Packet to reply to. */
@@ -1293,7 +1357,7 @@ void wins_process_name_query_request(struct subnet_record *subrec,
   struct name_record *namerec = NULL;
 
   DEBUG(3,("wins_process_name_query: name query for name %s from IP %s\n", 
-            namestr(question), inet_ntoa(p->ip) ));
+            nmb_namestr(question), inet_ntoa(p->ip) ));
 
   /*
    * Special name code. If the queried name is *<1b> then search
@@ -1316,10 +1380,10 @@ void wins_process_name_query_request(struct subnet_record *subrec,
      * If it's a DNSFAIL_NAME then reply name not found.
      */
 
-    if(namerec->source == DNSFAIL_NAME)
+    if( namerec->data.source == DNSFAIL_NAME )
     {
       DEBUG(3,("wins_process_name_query: name query for name %s returning DNS fail.\n",
-             namestr(question) ));
+             nmb_namestr(question) ));
       send_wins_name_query_response(NAM_ERR, p, namerec);
       return;
     }
@@ -1328,16 +1392,17 @@ void wins_process_name_query_request(struct subnet_record *subrec,
      * If the name has expired then reply name not found.
      */
 
-    if((namerec->death_time != PERMANENT_TTL) && (namerec->death_time < p->timestamp))
+    if( (namerec->data.death_time != PERMANENT_TTL)
+     && (namerec->data.death_time < p->timestamp) )
     {
       DEBUG(3,("wins_process_name_query: name query for name %s - name expired. Returning fail.\n",
-                namestr(question) ));
+                nmb_namestr(question) ));
       send_wins_name_query_response(NAM_ERR, p, namerec);
       return;
     }
 
     DEBUG(3,("wins_process_name_query: name query for name %s returning first IP %s.\n",
-           namestr(question), inet_ntoa(namerec->ip[0]) ));
+           nmb_namestr(question), inet_ntoa(namerec->data.ip[0]) ));
 
     send_wins_name_query_response(0, p, namerec);
     return;
@@ -1352,7 +1417,7 @@ void wins_process_name_query_request(struct subnet_record *subrec,
   {
 
     DEBUG(3,("wins_process_name_query: name query for name %s not found - doing dns lookup.\n",
-              namestr(question) ));
+              nmb_namestr(question) ));
 
     queue_dns_query(p, question, &namerec);
     return;
@@ -1412,12 +1477,12 @@ void wins_process_name_release_request(struct subnet_record *subrec,
 
     DEBUG(0,("wins_process_name_release_request: broadcast name registration request \
 received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n",
-          namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+          nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
     return;
   }
   
   DEBUG(3,("wins_process_name_release_request: %s name release for name %s \
-IP %s\n", releasing_group_name ? "Group" : "Unique", namestr(question), inet_ntoa(from_ip) ));
+IP %s\n", releasing_group_name ? "Group" : "Unique", nmb_namestr(question), inet_ntoa(from_ip) ));
     
   /*
    * Deal with policy regarding 0x1d names.
@@ -1426,7 +1491,7 @@ IP %s\n", releasing_group_name ? "Group" : "Unique", namestr(question), inet_nto
   if(!releasing_group_name && (question->name_type == 0x1d))
   {
     DEBUG(3,("wins_process_name_release_request: Ignoring request \
-to release name %s from IP %s.", namestr(question), inet_ntoa(p->ip) ));
+to release name %s from IP %s.", nmb_namestr(question), inet_ntoa(p->ip) ));
     send_wins_name_release_response(0, p);
     return;
   }
@@ -1437,7 +1502,8 @@ to release name %s from IP %s.", namestr(question), inet_ntoa(p->ip) ));
     
   namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
 
-  if((namerec == NULL) || ((namerec != NULL) && (namerec->source != REGISTER_NAME)) )
+  if( (namerec == NULL)
+   || ((namerec != NULL) && (namerec->data.source != REGISTER_NAME)) )
   {
     send_wins_name_release_response(NAM_ERR, p);
     return;
@@ -1464,7 +1530,7 @@ to release name %s from IP %s.", namestr(question), inet_ntoa(p->ip) ));
   {
     DEBUG(3,("wins_process_name_release_request: Refusing request to \
 release name %s as IP %s is not one of the known IP's for this name.\n",
-           namestr(question), inet_ntoa(from_ip) ));
+           nmb_namestr(question), inet_ntoa(from_ip) ));
     send_wins_name_release_response(NAM_ERR, p);
     return;
   }    
@@ -1476,10 +1542,12 @@ release name %s as IP %s is not one of the known IP's for this name.\n",
   send_wins_name_release_response(0, p);
   remove_ip_from_name_record(namerec, from_ip);
 
+  wins_hook("delete", namerec, 0);
+
   /* 
    * Remove the name entirely if no IP addresses left.
    */
-  if (namerec->num_ips == 0)
+  if (namerec->data.num_ips == 0)
     remove_name_from_namelist(subrec, namerec);
 
 }
@@ -1494,16 +1562,18 @@ void initiate_wins_processing(time_t t)
 
   if (!lasttime)
     lasttime = t;
-  if (t - lasttime < 5)
+  if (t - lasttime < 20)
     return;
 
+  lasttime = t;
+
   if(!lp_we_are_a_wins_server())
     return;
 
   expire_names_on_subnet(wins_server_subnet, t);
 
   if(wins_server_subnet->namelist_changed)
-    wins_write_database();
+    wins_write_database(True);
 
   wins_server_subnet->namelist_changed = False;
 }
@@ -1511,65 +1581,87 @@ void initiate_wins_processing(time_t t)
 /*******************************************************************
  Write out the current WINS database.
 ******************************************************************/
-
-void wins_write_database(void)
+void wins_write_database(BOOL background)
 {
   struct name_record *namerec;
   pstring fname, fnamenew;
-   
+
   FILE *fp;
    
   if(!lp_we_are_a_wins_server())
     return;
 
-  fstrcpy(fname,lp_lockdir());
-  trim_string(fname,NULL,"/");
-  pstrcat(fname,"/");
-  pstrcat(fname,WINS_LIST);
-  pstrcpy(fnamenew,fname);
-  pstrcat(fnamenew,".");
+  /* we will do the writing in a child process to ensure that the parent
+     doesn't block while this is done */
+  if (background) {
+         CatchChild();
+         if (fork()) {
+                 return;
+         }
+  }
 
-  if((fp = fopen(fnamenew,"w")) == NULL)
+  slprintf(fname,sizeof(fname),"%s/%s", lp_lockdir(), WINS_LIST);
+  all_string_sub(fname,"//", "/", 0);
+  slprintf(fnamenew,sizeof(fnamenew),"%s.%u", fname, (unsigned int)getpid());
+
+  if((fp = sys_fopen(fnamenew,"w")) == NULL)
   {
     DEBUG(0,("wins_write_database: Can't open %s. Error was %s\n", fnamenew, strerror(errno)));
+    if (background) {
+           _exit(0);
+    }
     return;
   }
 
   DEBUG(4,("wins_write_database: Dump of WINS name list.\n"));
+
+  fprintf(fp,"VERSION %d %u\n", WINS_VERSION, wins_hash());
  
-  for (namerec = wins_server_subnet->namelist; namerec; namerec = namerec->next)
+  for( namerec 
+           = (struct name_record *)ubi_trFirst( wins_server_subnet->namelist );
+       namerec;
+       namerec = (struct name_record *)ubi_trNext( namerec ) )
   {
     int i;
     struct tm *tm;
 
-    DEBUG(4,("%-19s ", namestr(&namerec->name) ));
+    DEBUGADD(4,("%-19s ", nmb_namestr(&namerec->name) ));
 
-    if(namerec->death_time != PERMANENT_TTL)
+    if( namerec->data.death_time != PERMANENT_TTL )
     {
-      tm = LocalTime(&namerec->death_time);
-      DEBUG(4,("TTL = %s", asctime(tm) ));
+      char *ts, *nl;
+
+      tm = LocalTime(&namerec->data.death_time);
+      ts = asctime(tm);
+      nl = strrchr( ts, '\n' );
+      if( NULL != nl )
+        *nl = '\0';
+      DEBUGADD(4,("TTL = %s  ", ts ));
     }
     else
-      DEBUG(4,("TTL = PERMANENT\t"));
+      DEBUGADD(4,("TTL = PERMANENT                 "));
 
-    for (i = 0; i < namerec->num_ips; i++)
-      DEBUG(4,("%15s ", inet_ntoa(namerec->ip[i]) ));
-    DEBUG(4,("%2x\n", namerec->nb_flags ));
+    for (i = 0; i < namerec->data.num_ips; i++)
+      DEBUGADD(4,("%15s ", inet_ntoa(namerec->data.ip[i]) ));
+    DEBUGADD(4,("%2x\n", namerec->data.nb_flags ));
 
-    if (namerec->source == REGISTER_NAME)
+    if( namerec->data.source == REGISTER_NAME )
     {
       fprintf(fp, "\"%s#%02x\" %d ",
              namerec->name.name,namerec->name.name_type, /* Ignore scope. */
-             (int)namerec->death_time);
+             (int)namerec->data.death_time);
 
-      for (i = 0; i < namerec->num_ips; i++)
-        fprintf(fp, "%s ", inet_ntoa(namerec->ip[i]));
-      fprintf(fp, "%2xR\n", namerec->nb_flags);
+      for (i = 0; i < namerec->data.num_ips; i++)
+        fprintf( fp, "%s ", inet_ntoa( namerec->data.ip[i] ) );
+      fprintf( fp, "%2xR\n", namerec->data.nb_flags );
     }
   }
   
   fclose(fp);
-  unlink(fname);
   chmod(fnamenew,0644);
+  unlink(fname);
   rename(fnamenew,fname);
+  if (background) {
+         _exit(0);
+  }
 }