r12426: w2k3 refused 0x1B names registered as group names
[samba.git] / source / nbt_server / wins / winsserver.c
index 19b9c6c47ff50955a33e75cf6fb528730ec46cd7..9bbd8c643202322ded37db4b8dd83dd35d9d0b3d 100644 (file)
@@ -4,7 +4,8 @@
    core wins server handling
 
    Copyright (C) Andrew Tridgell       2005
-   
+   Copyright (C) Stefan Metzmacher     2005
+      
    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
@@ -31,8 +32,8 @@
 */
 uint32_t wins_server_ttl(struct wins_server *winssrv, uint32_t ttl)
 {
-       ttl = MIN(ttl, winssrv->max_ttl);
-       ttl = MAX(ttl, winssrv->min_ttl);
+       ttl = MIN(ttl, winssrv->config.max_renew_interval);
+       ttl = MAX(ttl, winssrv->config.min_renew_interval);
        return ttl;
 }
 
@@ -56,7 +57,8 @@ static enum wrepl_name_type wrepl_type(uint16_t nb_flags, struct nbt_name *name,
 */
 static uint8_t wins_register_new(struct nbt_name_socket *nbtsock, 
                                 struct nbt_name_packet *packet, 
-                                const struct nbt_peer_socket *src)
+                                const struct nbt_peer_socket *src,
+                                enum wrepl_name_type type)
 {
        struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, 
                                                       struct nbtd_interface);
@@ -66,14 +68,11 @@ static uint8_t wins_register_new(struct nbt_name_socket *nbtsock,
        uint16_t nb_flags = packet->additional[0].rdata.netbios.addresses[0].nb_flags;
        const char *address = packet->additional[0].rdata.netbios.addresses[0].ipaddr;
        struct winsdb_record rec;
-       enum wrepl_name_type type;
        enum wrepl_name_node node;
-       BOOL mhomed = ((packet->operation & NBT_OPCODE) == NBT_OPCODE_MULTI_HOME_REG);
 
 #define WREPL_NODE_NBT_FLAGS(nb_flags) \
        ((nb_flags & NBT_NM_OWNER_TYPE)>>13)
 
-       type    = wrepl_type(nb_flags, name, mhomed);
        node    = WREPL_NODE_NBT_FLAGS(nb_flags);
 
        rec.name                = name;
@@ -107,6 +106,7 @@ static uint8_t wins_register_new(struct nbt_name_socket *nbtsock,
 static uint8_t wins_update_ttl(struct nbt_name_socket *nbtsock, 
                               struct nbt_name_packet *packet, 
                               struct winsdb_record *rec,
+                              struct winsdb_addr *winsdb_addr,
                               const struct nbt_peer_socket *src)
 {
        struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, 
@@ -114,17 +114,53 @@ static uint8_t wins_update_ttl(struct nbt_name_socket *nbtsock,
        struct wins_server *winssrv = iface->nbtsrv->winssrv;
        uint32_t ttl = wins_server_ttl(winssrv, packet->additional[0].ttl);
        const char *address = packet->additional[0].rdata.netbios.addresses[0].ipaddr;
-       time_t now = time(NULL);
+       uint32_t modify_flags = 0;
 
-       if (now + ttl > rec->expire_time) {
-               rec->expire_time   = now + ttl;
-       }
+       rec->expire_time   = time(NULL) + ttl;
        rec->registered_by = src->addr;
 
+       if (winsdb_addr) {
+               winsdb_addr->wins_owner  = WINSDB_OWNER_LOCAL;
+               winsdb_addr->expire_time = rec->expire_time;
+       }
+
+       if (strcmp(WINSDB_OWNER_LOCAL, rec->wins_owner) != 0) {
+               modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
+       }
+
        DEBUG(5,("WINS: refreshed registration of %s at %s\n",
                 nbt_name_string(packet, rec->name), address));
        
-       return winsdb_modify(winssrv->wins_db, rec, 0);
+       return winsdb_modify(winssrv->wins_db, rec, modify_flags);
+}
+
+/*
+  do a sgroup merge
+*/
+static uint8_t wins_sgroup_merge(struct nbt_name_socket *nbtsock, 
+                                struct nbt_name_packet *packet, 
+                                struct winsdb_record *rec,
+                                const char *address,
+                                const struct nbt_peer_socket *src)
+{
+       struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, 
+                                                      struct nbtd_interface);
+       struct wins_server *winssrv = iface->nbtsrv->winssrv;
+       uint32_t ttl = wins_server_ttl(winssrv, packet->additional[0].ttl);
+
+       rec->expire_time   = time(NULL) + ttl;
+       rec->registered_by = src->addr;
+
+       rec->addresses     = winsdb_addr_list_add(rec->addresses,
+                                                 address,
+                                                 WINSDB_OWNER_LOCAL,
+                                                 rec->expire_time);
+       if (rec->addresses == NULL) return NBT_RCODE_SVR;
+
+       DEBUG(5,("WINS: sgroup merge of %s at %s\n",
+                nbt_name_string(packet, rec->name), address));
+       
+       return winsdb_modify(winssrv->wins_db, rec, WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP);
 }
 
 /*
@@ -143,10 +179,23 @@ 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);
+       enum wrepl_name_type new_type = wrepl_type(nb_flags, name, mhomed);
+       struct winsdb_addr *winsdb_addr = NULL;
 
-       /* as a special case, the local master browser name is always accepted
-          for registration, but never stored */
-       if (name->type == NBT_NAME_MASTER) {
+       /*
+        * as a special case, the local master browser name is always accepted
+        * for registration, but never stored, but w2k3 stores it if it's registered
+        * as a group name, (but a query for the 0x1D name still returns not found!)
+        */
+       if (name->type == NBT_NAME_MASTER && !(nb_flags & NBT_NM_GROUP)) {
+               rcode = NBT_RCODE_OK;
+               goto done;
+       }
+
+       /* w2k3 refuses 0x1B names with marked as group */
+       if (name->type == NBT_NAME_PDC && (nb_flags & NBT_NM_GROUP)) {
+               rcode = NBT_RCODE_RFS;
                goto done;
        }
 
@@ -156,69 +205,122 @@ static void nbtd_winsserver_register(struct nbt_name_socket *nbtsock,
                goto done;
        }
 
+       /* w2k3 refuses 0x1E names with out marked as group */
+       if (name->type == NBT_NAME_BROWSER && !(nb_flags & NBT_NM_GROUP)) {
+               rcode = NBT_RCODE_RFS;
+               goto done;
+       }
+
        status = winsdb_lookup(winssrv->wins_db, name, packet, &rec);
        if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) {
-               rcode = wins_register_new(nbtsock, packet, src);
+               rcode = wins_register_new(nbtsock, packet, src, new_type);
                goto done;
        } else if (!NT_STATUS_IS_OK(status)) {
                rcode = NBT_RCODE_SVR;
                goto done;
-       } else if (rec->state != WREPL_STATE_ACTIVE) {
-/* TODO: this is not always correct!!!*/
+       } else if (rec->is_static) {
+               if (rec->type == WREPL_TYPE_GROUP || rec->type == WREPL_TYPE_SGROUP) {
+                       rcode = NBT_RCODE_OK;
+                       goto done;
+               }
+               rcode = NBT_RCODE_ACT;
+               goto done;
+       }
+
+       if (rec->type == WREPL_TYPE_GROUP) {
+               if (new_type != WREPL_TYPE_GROUP) {
+                       DEBUG(2,("WINS: Attempt to register name %s as non normal group(%u)"
+                                " while a normal group is already there\n",
+                                nbt_name_string(packet, name), new_type));
+                       rcode = NBT_RCODE_ACT;
+                       goto done;
+               }
+
+               if (rec->state == WREPL_STATE_ACTIVE) {
+                       /* TODO: is this correct? */
+                       rcode = wins_update_ttl(nbtsock, packet, rec, NULL, src);
+                       goto done;
+               }
+
+               /* TODO: is this correct? */
                winsdb_delete(winssrv->wins_db, rec);
-               rcode = wins_register_new(nbtsock, packet, src);
+               rcode = wins_register_new(nbtsock, packet, src, new_type);
                goto done;
        }
 
-       /* its an active name - first see if the registration is of the right type */
-       if ((rec->type == WREPL_TYPE_GROUP) && !(nb_flags & NBT_NM_GROUP)) {
-               DEBUG(2,("WINS: Attempt to register unique name %s when group name is active\n",
-                        nbt_name_string(packet, name)));
-               rcode = NBT_RCODE_ACT;
+       if (rec->state != WREPL_STATE_ACTIVE) {
+               winsdb_delete(winssrv->wins_db, rec);
+               rcode = wins_register_new(nbtsock, packet, src, new_type);
                goto done;
        }
 
-       /* if its an active unique name, and the registration is for a group, then
-          see if the unique name owner still wants the name */
-       if (!(rec->type == WREPL_TYPE_GROUP) && (nb_flags & NBT_NM_GROUP)) {
+       switch (rec->type) {
+       case WREPL_TYPE_UNIQUE:
+       case WREPL_TYPE_MHOMED:
+               /* 
+                * if its an active unique name, and the registration is for a group, then
+                * see if the unique name owner still wants the name
+                * TODO: is this correct?
+                */
+               if (new_type == WREPL_TYPE_GROUP || new_type == WREPL_TYPE_GROUP) {
+                       wins_register_wack(nbtsock, packet, rec, src);
+                       return;
+               }
+
+               /* 
+                * if the registration is for an address that is currently active, then 
+                * just update the expiry time of the record and the address
+                * TODO: is this correct?
+                */
+               winsdb_addr = winsdb_addr_list_check(rec->addresses, address);
+               if (winsdb_addr) {
+                       rcode = wins_update_ttl(nbtsock, packet, rec, winsdb_addr, src);
+                       goto done;
+               }
+
+               /*
+                * we have to do a WACK to see if the current owner is willing
+                * to give up its claim
+                */
                wins_register_wack(nbtsock, packet, rec, src);
                return;
-       }
 
-       /* if the registration is for a group, then just update the expiry time 
-          and we are done */
-       if (nb_flags & NBT_NM_GROUP) {
-               wins_update_ttl(nbtsock, packet, rec, src);
+       case WREPL_TYPE_GROUP:
+               /* this should not be reached as normal groups are handled above */
+               DEBUG(0,("BUG at %s\n",__location__));
+               rcode = NBT_RCODE_ACT;
                goto done;
-       }
 
-       /*
-        * TODO: this complete functions needs a lot of work,
-        *       to handle special group and multiomed registrations
-        */
-       if (name->type == NBT_NAME_LOGON) {
-               wins_update_ttl(nbtsock, packet, rec, src);
-               goto done;
-       }
+       case WREPL_TYPE_SGROUP:
+               /* if the new record isn't also a special group, refuse the registration */ 
+               if (new_type != WREPL_TYPE_SGROUP) {
+                       DEBUG(2,("WINS: Attempt to register name %s as non special group(%u)"
+                                " while a special group is already there\n",
+                                nbt_name_string(packet, name), new_type));
+                       rcode = NBT_RCODE_ACT;
+                       goto done;
+               }
+
+               /* 
+                * if the registration is for an address that is currently active, then 
+                * just update the expiry time
+                * just update the expiry time of the record and the address
+                * TODO: is this correct?
+                */
+               winsdb_addr = winsdb_addr_list_check(rec->addresses, address);
+               if (winsdb_addr) {
+                       rcode = wins_update_ttl(nbtsock, packet, rec, winsdb_addr, src);
+                       goto done;
+               }
 
-       /* if the registration is for an address that is currently active, then 
-          just update the expiry time */
-       if (winsdb_addr_list_check(rec->addresses, address)) {
-               wins_update_ttl(nbtsock, packet, rec, src);
+               rcode = wins_sgroup_merge(nbtsock, packet, rec, address, src);
                goto done;
        }
 
-       /* we have to do a WACK to see if the current owner is willing
-          to give up its claim */      
-       wins_register_wack(nbtsock, packet, rec, src);
-       return;
-
 done:
        nbtd_name_registration_reply(nbtsock, packet, src, rcode);
 }
 
-
-
 /*
   query a name
 */
@@ -235,12 +337,20 @@ static void nbtd_winsserver_query(struct nbt_name_socket *nbtsock,
        const char **addresses;
        uint16_t nb_flags = 0; /* TODO: ... */
 
+       if (name->type == NBT_NAME_MASTER) {
+               goto notfound;
+       }
+
        status = winsdb_lookup(winssrv->wins_db, name, packet, &rec);
-       if (!NT_STATUS_IS_OK(status) || rec->state != WREPL_STATE_ACTIVE) {
-               nbtd_negative_name_query_reply(nbtsock, packet, src);
-               return;
+       if (!NT_STATUS_IS_OK(status)) {
+               goto notfound;
        }
 
+       /*
+        * for group's we always reply with
+        * 255.255.255.255 as address, even if
+        * the record is released or tombstoned
+        */
        if (rec->type == WREPL_TYPE_GROUP) {
                addresses = talloc_array(packet, const char *, 2);
                if (addresses == NULL) {
@@ -249,16 +359,24 @@ static void nbtd_winsserver_query(struct nbt_name_socket *nbtsock,
                }
                addresses[0] = WINSDB_GROUP_ADDRESS;
                addresses[1] = NULL;
-       } else {
-               addresses = winsdb_addr_string_list(packet, rec->addresses);
-               if (addresses == NULL) {
-                       nbtd_negative_name_query_reply(nbtsock, packet, src);
-                       return; 
-               }
+               goto found;
+       }
+
+       if (rec->state != WREPL_STATE_ACTIVE) {
+               goto notfound;
        }
 
+       addresses = winsdb_addr_string_list(packet, rec->addresses);
+       if (!addresses) {
+               goto notfound;
+       }
+found:
        nbtd_name_query_reply(nbtsock, packet, src, name, 
                              0, nb_flags, addresses);
+       return;
+
+notfound:
+       nbtd_negative_name_query_reply(nbtsock, packet, src);
 }
 
 /*
@@ -274,27 +392,77 @@ static void nbtd_winsserver_release(struct nbt_name_socket *nbtsock,
        struct wins_server *winssrv = iface->nbtsrv->winssrv;
        struct nbt_name *name = &packet->questions[0].name;
        struct winsdb_record *rec;
+       uint32_t modify_flags = 0;
+       uint8_t ret;
 
        status = winsdb_lookup(winssrv->wins_db, name, packet, &rec);
-       if (!NT_STATUS_IS_OK(status) || 
-           rec->state != WREPL_STATE_ACTIVE || 
-           rec->type == WREPL_TYPE_GROUP) {
+       if (!NT_STATUS_IS_OK(status)) {
+               goto done;
+       }
+
+       if (rec->is_static) {
+               if (rec->type == WREPL_TYPE_UNIQUE || rec->type == WREPL_TYPE_MHOMED) {
+                       goto done;
+               }
+               nbtd_name_release_reply(nbtsock, packet, src, NBT_RCODE_ACT);
+               return;
+       }
+
+       if (rec->state != WREPL_STATE_ACTIVE) {
+               goto done;
+       }
+
+       /* 
+        * TODO: do we need to check if
+        *       src->addr matches packet->additional[0].rdata.netbios.addresses[0].ipaddr
+        *       here?
+        */
+
+       /* 
+        * we only allow releases from an owner - other releases are
+        * silently ignored
+        */
+       if (!winsdb_addr_list_check(rec->addresses, src->addr)) {
                goto done;
        }
 
-       /* we only allow releases from an owner - other releases are
-          silently ignored */
-       if (winsdb_addr_list_check(rec->addresses, src->addr)) {
-               const char *address = packet->additional[0].rdata.netbios.addresses[0].ipaddr;
+       DEBUG(4,("WINS: released name %s from %s\n", nbt_name_string(rec, rec->name), src->addr));
 
-               DEBUG(4,("WINS: released name %s at %s\n", nbt_name_string(rec, rec->name), address));
-               winsdb_addr_list_remove(rec->addresses, address);
-               if (rec->addresses[0] == NULL) {
+       switch (rec->type) {
+       case WREPL_TYPE_UNIQUE:
+               rec->state = WREPL_STATE_RELEASED;
+               break;
+
+       case WREPL_TYPE_GROUP:
+               rec->state = WREPL_STATE_RELEASED;
+               break;
+
+       case WREPL_TYPE_SGROUP:
+               winsdb_addr_list_remove(rec->addresses, src->addr);
+               /* TODO: do we need to take the ownership here? */
+               if (winsdb_addr_list_length(rec->addresses) == 0) {
                        rec->state = WREPL_STATE_RELEASED;
                }
-               winsdb_modify(winssrv->wins_db, rec, 0);
+               break;
+
+       case WREPL_TYPE_MHOMED:
+               winsdb_addr_list_remove(rec->addresses, src->addr);
+               /* TODO: do we need to take the ownership here? */
+               if (winsdb_addr_list_length(rec->addresses) == 0) {
+                       rec->state = WREPL_STATE_RELEASED;
+               }
+               break;
+       }
+
+       if (rec->state == WREPL_STATE_RELEASED) {
+               rec->expire_time = time(NULL) + winssrv->config.tombstone_interval;
        }
 
+       ret = winsdb_modify(winssrv->wins_db, rec, modify_flags);
+       if (ret != NBT_RCODE_OK) {
+               DEBUG(1,("WINS: FAILED: released name %s at %s: error:%u\n",
+                       nbt_name_string(rec, rec->name), src->addr, ret));
+       }
 done:
        /* we match w2k3 by always giving a positive reply to name releases. */
        nbtd_name_release_reply(nbtsock, packet, src, NBT_RCODE_OK);
@@ -339,6 +507,8 @@ void nbtd_winsserver_request(struct nbt_name_socket *nbtsock,
 */
 NTSTATUS nbtd_winsserver_init(struct nbtd_server *nbtsrv)
 {
+       uint32_t tombstone_interval;
+
        if (!lp_wins_support()) {
                nbtsrv->winssrv = NULL;
                return NT_STATUS_OK;
@@ -347,8 +517,10 @@ 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->max_ttl     = lp_max_wins_ttl();
-       nbtsrv->winssrv->min_ttl     = lp_min_wins_ttl();
+       nbtsrv->winssrv->config.max_renew_interval = lp_max_wins_ttl();
+       nbtsrv->winssrv->config.min_renew_interval = lp_min_wins_ttl();
+       tombstone_interval = lp_parm_int(-1,"wreplsrv","tombstone_interval", 6*24*60*60);
+       nbtsrv->winssrv->config.tombstone_interval = tombstone_interval;
 
        nbtsrv->winssrv->wins_db     = winsdb_connect(nbtsrv->winssrv);
        if (!nbtsrv->winssrv->wins_db) {