r12426: w2k3 refused 0x1B names registered as group names
[samba.git] / source / nbt_server / wins / winsserver.c
index d5043caa298428986d8e296c26d2510c53877d50..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
 */
 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;
 }
 
+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) {
+               return WREPL_TYPE_SGROUP;
+       }
+       if (nb_flags & NBT_NM_GROUP) {
+               return WREPL_TYPE_GROUP;
+       }
+       if (mhomed) {
+               return WREPL_TYPE_MHOMED;
+       }
+       return WREPL_TYPE_UNIQUE;
+}
+
 /*
   register a new name with WINS
 */
 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);
@@ -51,19 +68,25 @@ 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;
-
-       rec.name          = name;
-       rec.nb_flags      = nb_flags;
-       rec.state         = WINS_REC_ACTIVE;
-       rec.wins_owner    = WINSDB_OWNER_LOCAL;
-       rec.expire_time   = time(NULL) + ttl;
-       rec.registered_by = src->addr;
-       rec.addresses     = winsdb_addr_list_make(packet);
+       enum wrepl_name_node node;
+
+#define WREPL_NODE_NBT_FLAGS(nb_flags) \
+       ((nb_flags & NBT_NM_OWNER_TYPE)>>13)
+
+       node    = WREPL_NODE_NBT_FLAGS(nb_flags);
+
+       rec.name                = name;
+       rec.type                = type;
+       rec.state               = WREPL_STATE_ACTIVE;
+       rec.node                = node;
+       rec.is_static           = False;
+       rec.expire_time         = time(NULL) + ttl;
+       rec.version             = 0; /* will allocated later */
+       rec.wins_owner          = NULL; /* will be set later */
+       rec.registered_by       = src->addr;
+       rec.addresses           = winsdb_addr_list_make(packet);
        if (rec.addresses == NULL) return NBT_RCODE_SVR;
 
-       if (IS_GROUP_NAME(name, nb_flags)) {
-               address = WINSDB_GROUP_ADDRESS;
-       }
        rec.addresses     = winsdb_addr_list_add(rec.addresses,
                                                 address,
                                                 WINSDB_OWNER_LOCAL,
@@ -73,7 +96,7 @@ static uint8_t wins_register_new(struct nbt_name_socket *nbtsock,
        DEBUG(4,("WINS: accepted registration of %s with address %s\n",
                 nbt_name_string(packet, name), rec.addresses[0]->address));
        
-       return winsdb_add(winssrv, &rec);
+       return winsdb_add(winssrv->wins_db, &rec, WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP);
 }
 
 
@@ -83,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, 
@@ -90,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, rec);
+       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);
 }
 
 /*
@@ -119,66 +179,148 @@ 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, 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;
+       }
 
-       /* as a special case, the local master browser name is always accepted
-          for registration, but never stored */
-       if (name->type == NBT_NAME_MASTER) {
+       /* 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;
+       }
+
+       /* w2k3 refuses 0x1C names with out marked as group */
+       if (name->type == NBT_NAME_LOGON && !(nb_flags & NBT_NM_GROUP)) {
+               rcode = NBT_RCODE_RFS;
+               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 != WINS_REC_ACTIVE) {
-               winsdb_delete(winssrv, rec);
-               rcode = wins_register_new(nbtsock, packet, src);
+       } 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;
        }
 
-       /* its an active name - first see if the registration is of the right type */
-       if ((rec->nb_flags & NBT_NM_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->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, new_type);
+               goto done;
+       }
+
+       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->nb_flags & NBT_NM_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 (IS_GROUP_NAME(name, nb_flags)) {
-               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;
-       }
 
-       /* 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);
+       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;
+               }
+
+               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
 */
@@ -193,21 +335,48 @@ static void nbtd_winsserver_query(struct nbt_name_socket *nbtsock,
        struct nbt_name *name = &packet->questions[0].name;
        struct winsdb_record *rec;
        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 != WINS_REC_ACTIVE) {
-               nbtd_negative_name_query_reply(nbtsock, packet, src);
-               return;
+       if (!NT_STATUS_IS_OK(status)) {
+               goto notfound;
        }
 
-       addresses = winsdb_addr_string_list(packet, rec->addresses);
-       if (addresses == NULL) {
-               nbtd_negative_name_query_reply(nbtsock, packet, src);
-               return; 
+       /*
+        * 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) {
+                       nbtd_negative_name_query_reply(nbtsock, packet, src);
+                       return;
+               }
+               addresses[0] = WINSDB_GROUP_ADDRESS;
+               addresses[1] = NULL;
+               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, rec->nb_flags, addresses);
+                             0, nb_flags, addresses);
+       return;
+
+notfound:
+       nbtd_negative_name_query_reply(nbtsock, packet, src);
 }
 
 /*
@@ -223,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 != WINS_REC_ACTIVE || 
-           IS_GROUP_NAME(name, rec->nb_flags)) {
+       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) {
-                       rec->state = WINS_REC_RELEASED;
+       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;
+               }
+               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;
                }
-               winsdb_modify(winssrv, rec);
+               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);
@@ -288,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;
@@ -296,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) {