r25624: Remove ipv4_addr hack. Only causes 4 extra includes of system/network.h becau...
[bbaumbach/samba-autobuild/.git] / source4 / nbt_server / wins / winsserver.c
index 4d40b31ba651e9f77c12fc4ec2002fe7b87f90b6..bce80702df29dd5ea352a529934f4a9bf997ff7c 100644 (file)
@@ -8,7 +8,7 @@
       
    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
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
@@ -17,8 +17,7 @@
    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.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
 #include "system/time.h"
 #include "libcli/composite/composite.h"
 #include "smbd/service_task.h"
+#include "system/network.h"
 #include "lib/socket/socket.h"
+#include "lib/socket/netif.h"
 #include "lib/ldb/include/ldb.h"
+#include "param/param.h"
 
 /*
   work out the ttl we will use given a client requested ttl
@@ -42,7 +44,7 @@ uint32_t wins_server_ttl(struct wins_server *winssrv, uint32_t ttl)
        return ttl;
 }
 
-static enum wrepl_name_type wrepl_type(uint16_t nb_flags, struct nbt_name *name, BOOL mhomed)
+static enum wrepl_name_type wrepl_type(uint16_t nb_flags, struct nbt_name *name, bool mhomed)
 {
        /* this copes with the nasty hack that is the type 0x1c name */
        if (name->type == NBT_NAME_LOGON) {
@@ -84,7 +86,7 @@ static uint8_t wins_register_new(struct nbt_name_socket *nbtsock,
        rec.type                = type;
        rec.state               = WREPL_STATE_ACTIVE;
        rec.node                = node;
-       rec.is_static           = False;
+       rec.is_static           = false;
        rec.expire_time         = time(NULL) + ttl;
        rec.version             = 0; /* will be allocated later */
        rec.wins_owner          = NULL; /* will be set later */
@@ -97,7 +99,7 @@ static uint8_t wins_register_new(struct nbt_name_socket *nbtsock,
                                                 address,
                                                 winssrv->wins_db->local_owner,
                                                 rec.expire_time,
-                                                True);
+                                                true);
        if (rec.addresses == NULL) return NBT_RCODE_SVR;
 
        DEBUG(4,("WINS: accepted registration of %s with address %s\n",
@@ -132,7 +134,7 @@ static uint8_t wins_update_ttl(struct nbt_name_socket *nbtsock,
                                                      winsdb_addr->address,
                                                      winssrv->wins_db->local_owner,
                                                      rec->expire_time,
-                                                     True);
+                                                     true);
                if (rec->addresses == NULL) return NBT_RCODE_SVR;
        }
 
@@ -168,7 +170,7 @@ static uint8_t wins_sgroup_merge(struct nbt_name_socket *nbtsock,
                                                  address,
                                                  winssrv->wins_db->local_owner,
                                                  rec->expire_time,
-                                                 True);
+                                                 true);
        if (rec->addresses == NULL) return NBT_RCODE_SVR;
 
        DEBUG(5,("WINS: sgroup merge of %s at %s\n",
@@ -215,7 +217,7 @@ static void wins_wack_allow(struct wack_state *s)
        if (!NT_STATUS_IS_OK(status) ||
            rec2->version != rec->version ||
            strcmp(rec2->wins_owner, rec->wins_owner) != 0) {
-               DEBUG(1,("WINS: record %s changed during WACK - failing registration\n",
+               DEBUG(5,("WINS: record %s changed during WACK - failing registration\n",
                         nbt_name_string(s, rec->name)));
                wins_wack_deny(s);
                return;
@@ -247,11 +249,11 @@ static void wins_wack_allow(struct wack_state *s)
         * and update the time stamp and owner for the ownes that are still there
         */
        for (i=0; rec->addresses[i]; i++) {
-               BOOL found = False;
+               bool found = false;
                for (j=0; j < s->io.out.num_addresses; j++) {
                        if (strcmp(rec->addresses[i]->address, s->io.out.addresses[j]) != 0) continue;
 
-                       found = True;
+                       found = true;
                        break;
                }
                if (found) {
@@ -260,7 +262,7 @@ static void wins_wack_allow(struct wack_state *s)
                                                              s->reg_address,
                                                              s->winssrv->wins_db->local_owner,
                                                              rec->expire_time,
-                                                             True);
+                                                             true);
                        if (rec->addresses == NULL) goto failed;
                        continue;
                }
@@ -273,7 +275,7 @@ static void wins_wack_allow(struct wack_state *s)
                                              s->reg_address,
                                              s->winssrv->wins_db->local_owner,
                                              rec->expire_time,
-                                             True);
+                                             true);
        if (rec->addresses == NULL) goto failed;
 
        /* if we have more than one address, this becomes implicit a MHOMED record */
@@ -300,7 +302,7 @@ static void wack_wins_challenge_handler(struct composite_context *c_req)
 {
        struct wack_state *s = talloc_get_type(c_req->async.private_data,
                                               struct wack_state);
-       BOOL found;
+       bool found;
        uint32_t i;
 
        s->status = wins_challenge_recv(c_req, s, &s->io);
@@ -326,11 +328,11 @@ static void wack_wins_challenge_handler(struct composite_context *c_req)
         * with the address trying to be registered, then deny
         * the registration
         */
-       found = False;
+       found = false;
        for (i=0; i < s->io.out.num_addresses; i++) {
                if (strcmp(s->reg_address, s->io.out.addresses[i]) != 0) continue;
 
-               found = True;
+               found = true;
                break;
        }
        if (!found) {
@@ -419,7 +421,7 @@ static void nbtd_winsserver_register(struct nbt_name_socket *nbtsock,
        uint8_t rcode = NBT_RCODE_OK;
        uint16_t nb_flags = packet->additional[0].rdata.netbios.addresses[0].nb_flags;
        const char *address = packet->additional[0].rdata.netbios.addresses[0].ipaddr;
-       BOOL mhomed = ((packet->operation & NBT_OPCODE) == NBT_OPCODE_MULTI_HOME_REG);
+       bool mhomed = ((packet->operation & NBT_OPCODE) == NBT_OPCODE_MULTI_HOME_REG);
        enum wrepl_name_type new_type = wrepl_type(nb_flags, name, mhomed);
        struct winsdb_addr *winsdb_addr = NULL;
 
@@ -558,6 +560,105 @@ done:
        nbtd_name_registration_reply(nbtsock, packet, src, rcode);
 }
 
+static uint32_t ipv4_match_bits(struct in_addr ip1, struct in_addr ip2)
+{
+       uint32_t i, j, match=0;
+       uint8_t *p1, *p2;
+
+       p1 = (uint8_t *)&ip1.s_addr;
+       p2 = (uint8_t *)&ip2.s_addr;
+
+       for (i=0; i<4; i++) {
+               if (p1[i] != p2[i]) break;
+               match += 8;
+       }
+
+       if (i==4) return match;
+
+       for (j=0; j<8; j++) {
+               if ((p1[i] & (1<<(7-j))) != (p2[i] & (1<<(7-j))))
+                       break;
+               match++;
+       }
+
+       return match;
+}
+
+static int nbtd_wins_randomize1Clist_sort(void *p1,/* (const char **) */
+                                         void *p2,/* (const char **) */
+                                         struct socket_address *src)
+{
+       const char *a1 = (const char *)*(const char **)p1;
+       const char *a2 = (const char *)*(const char **)p2;
+       uint32_t match_bits1;
+       uint32_t match_bits2;
+
+       match_bits1 = ipv4_match_bits(interpret_addr2(a1), interpret_addr2(src->addr));
+       match_bits2 = ipv4_match_bits(interpret_addr2(a2), interpret_addr2(src->addr));
+
+       return match_bits2 - match_bits1;
+}
+
+static void nbtd_wins_randomize1Clist(const char **addresses, struct socket_address *src)
+{
+       const char *mask;
+       const char *tmp;
+       uint32_t num_addrs;
+       uint32_t idx, sidx;
+       int r;
+
+       for (num_addrs=0; addresses[num_addrs]; num_addrs++) { /* noop */ }
+
+       if (num_addrs <= 1) return; /* nothing to do */
+
+       /* first sort the addresses depending on the matching to the client */
+       ldb_qsort(addresses, num_addrs , sizeof(addresses[0]),
+                 src, (ldb_qsort_cmp_fn_t)nbtd_wins_randomize1Clist_sort);
+
+       mask = lp_parm_string(global_loadparm, NULL, "nbtd", "wins_randomize1Clist_mask");
+       if (!mask) {
+               mask = "255.255.255.0";
+       }
+
+       /* 
+        * choose a random address to be the first in the response to the client,
+        * preferr the addresses inside the nbtd:wins_randomize1Clist_mask netmask
+        */
+       r = random();
+       idx = sidx = r % num_addrs;
+
+       while (1) {
+               bool same;
+
+               /* if the current one is in the same subnet, use it */
+               same = iface_same_net(addresses[idx], src->addr, mask);
+               if (same) {
+                       sidx = idx;
+                       break;
+               }
+
+               /* we need to check for idx == 0, after checking for the same net */
+               if (idx == 0) break;
+               /* 
+                * if we haven't found an address in the same subnet, search in ones
+                * which match the client more
+                *
+                * some notes:
+                *
+                * it's not "idx = idx % r" but "idx = r % idx"
+                * because in "a % b" b is the allowed range
+                * and b-1 is the maximum possible result, so it must be decreasing
+                * and the above idx == 0 check breaks the while(1) loop.
+                */
+               idx = r % idx;
+       }
+
+       /* note sidx == 0 is also valid here ... */
+       tmp             = addresses[0];
+       addresses[0]    = addresses[sidx];
+       addresses[sidx] = tmp;
+}
+
 /*
   query a name
 */
@@ -590,7 +691,8 @@ static void nbtd_winsserver_query(struct nbt_name_socket *nbtsock,
         * Typ: Daten REG_DWORD
         * Value: 0 = deactivated, 1 = activated
         */
-       if (name->type == NBT_NAME_LOGON && lp_parm_bool(-1, "nbtd", "wins_prepend1Bto1Cqueries", True)) {
+       if (name->type == NBT_NAME_LOGON && 
+           lp_parm_bool(global_loadparm, NULL, "nbtd", "wins_prepend1Bto1Cqueries", true)) {
                struct nbt_name name_1b;
 
                name_1b = *name;
@@ -604,7 +706,7 @@ static void nbtd_winsserver_query(struct nbt_name_socket *nbtsock,
 
        status = winsdb_lookup(winssrv->wins_db, name, packet, &rec);
        if (!NT_STATUS_IS_OK(status)) {
-               if (!lp_wins_dns_proxy()) {
+               if (!lp_wins_dns_proxy(global_loadparm)) {
                        goto notfound;
                }
 
@@ -678,6 +780,18 @@ static void nbtd_winsserver_query(struct nbt_name_socket *nbtsock,
                nb_flags |= (rec->node <<13);
        }
 
+       /*
+        * since Windows 2000 Service Pack 2 there's on option to trigger this behavior:
+        *
+        * HKEY_LOCAL_MACHINE\System\CurrentControlset\Services\WINS\Parameters\Randomize1CList
+        * Typ: Daten REG_DWORD
+        * Value: 0 = deactivated, 1 = activated
+        */
+       if (name->type == NBT_NAME_LOGON && 
+           lp_parm_bool(global_loadparm, NULL, "nbtd", "wins_randomize1Clist", false)) {
+               nbtd_wins_randomize1Clist(addresses, src);
+       }
+
 found:
        nbtd_name_query_reply(nbtsock, packet, src, name, 
                              0, nb_flags, addresses);
@@ -843,7 +957,7 @@ NTSTATUS nbtd_winsserver_init(struct nbtd_server *nbtsrv)
 {
        uint32_t tmp;
 
-       if (!lp_wins_support()) {
+       if (!lp_wins_support(global_loadparm)) {
                nbtsrv->winssrv = NULL;
                return NT_STATUS_OK;
        }
@@ -851,11 +965,11 @@ NTSTATUS nbtd_winsserver_init(struct nbtd_server *nbtsrv)
        nbtsrv->winssrv = talloc_zero(nbtsrv, struct wins_server);
        NT_STATUS_HAVE_NO_MEMORY(nbtsrv->winssrv);
 
-       nbtsrv->winssrv->config.max_renew_interval = lp_max_wins_ttl();
-       nbtsrv->winssrv->config.min_renew_interval = lp_min_wins_ttl();
-       tmp = lp_parm_int(-1,"wreplsrv","tombstone_interval", 6*24*60*60);
+       nbtsrv->winssrv->config.max_renew_interval = lp_max_wins_ttl(global_loadparm);
+       nbtsrv->winssrv->config.min_renew_interval = lp_min_wins_ttl(global_loadparm);
+       tmp = lp_parm_int(global_loadparm, NULL, "wreplsrv", "tombstone_interval", 6*24*60*60);
        nbtsrv->winssrv->config.tombstone_interval = tmp;
-       tmp = lp_parm_int(-1,"wreplsrv","tombstone_timeout", 1*24*60*60);
+       tmp = lp_parm_int(global_loadparm, NULL, "wreplsrv"," tombstone_timeout", 1*24*60*60);
        nbtsrv->winssrv->config.tombstone_timeout = tmp;
 
        nbtsrv->winssrv->wins_db     = winsdb_connect(nbtsrv->winssrv, WINSDB_HANDLE_CALLER_NBTD);