Merge branch 'master' of ssh://jra@git.samba.org/data/git/samba
[kai/samba.git] / source4 / wrepl_server / wrepl_in_call.c
index 8dabc2ee8604175f5a664bc6045b977d3b50a30e..ac44b4d75d3cbed039247947ca790f5c007ff04c 100644 (file)
@@ -7,7 +7,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,
@@ -16,8 +16,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"
@@ -30,6 +29,7 @@
 #include "nbt_server/wins/winsdb.h"
 #include "lib/ldb/include/ldb.h"
 #include "lib/ldb/include/ldb_errors.h"
+#include "system/time.h"
 
 static NTSTATUS wreplsrv_in_start_association(struct wreplsrv_in_call *call)
 {
@@ -49,12 +49,19 @@ static NTSTATUS wreplsrv_in_start_association(struct wreplsrv_in_call *call)
                return NT_STATUS_OK;
        }
 
+/*
+ * it seems that we don't know all details about the start_association
+ * to support replication with NT4 (it sends 1.1 instead of 5.2)
+ * we ignore the version numbers until we know all details
+ */
+#if 0
        if (start->minor_version != 2 || start->major_version != 5) {
                /* w2k terminate the connection if the versions doesn't match */
                return NT_STATUS_UNKNOWN_REVISION;
        }
+#endif
 
-       call->wreplconn->assoc_ctx.stopped      = False;
+       call->wreplconn->assoc_ctx.stopped      = false;
        call->wreplconn->assoc_ctx.our_ctx      = WREPLSRV_VALID_ASSOC_CTX;
        call->wreplconn->assoc_ctx.peer_ctx     = start->assoc_ctx;
 
@@ -63,6 +70,19 @@ static NTSTATUS wreplsrv_in_start_association(struct wreplsrv_in_call *call)
        start_reply->minor_version              = 2;
        start_reply->major_version              = 5;
 
+       /*
+        * nt4 uses 41 bytes for the start_association call
+        * so do it the same and as we don't know the meanings of this bytes
+        * we just send zeros and nt4, w2k and w2k3 seems to be happy with this
+        *
+        * if we don't do this nt4 uses an old version of the wins replication protocol
+        * and that would break nt4 <-> samba replication
+        */
+       call->rep_packet.padding                = data_blob_talloc(call, NULL, 21);
+       NT_STATUS_HAVE_NO_MEMORY(call->rep_packet.padding.data);
+
+       memset(call->rep_packet.padding.data, 0, call->rep_packet.padding.length);
+
        return NT_STATUS_OK;
 }
 
@@ -70,7 +90,7 @@ static NTSTATUS wreplsrv_in_stop_assoc_ctx(struct wreplsrv_in_call *call)
 {
        struct wrepl_stop *stop_out             = &call->rep_packet.message.stop;
 
-       call->wreplconn->assoc_ctx.stopped      = True;
+       call->wreplconn->assoc_ctx.stopped      = true;
 
        call->rep_packet.mess_type              = WREPL_STOP_ASSOCIATION;
        stop_out->reason                        = 4;
@@ -100,7 +120,7 @@ static NTSTATUS wreplsrv_in_stop_association(struct wreplsrv_in_call *call)
        }
 
        /* this will cause to not receive packets anymore and terminate the connection if the reply is send */
-       call->terminate_after_send = True;
+       call->terminate_after_send = true;
        return wreplsrv_in_stop_assoc_ctx(call);
 }
 
@@ -113,7 +133,7 @@ static NTSTATUS wreplsrv_in_table_query(struct wreplsrv_in_call *call)
        repl_out->command = WREPL_REPL_TABLE_REPLY;
 
        return wreplsrv_fill_wrepl_table(service, call, table_out,
-                                        service->wins_db->local_owner, True);
+                                        service->wins_db->local_owner, true);
 }
 
 static int wreplsrv_in_sort_wins_name(struct wrepl_wins_name *n1,
@@ -178,7 +198,8 @@ static NTSTATUS wreplsrv_in_send_request(struct wreplsrv_in_call *call)
        struct wrepl_wins_name *names;
        struct winsdb_record *rec;
        NTSTATUS status;
-       uint32_t i;
+       uint32_t i, j;
+       time_t now = time(NULL);
 
        owner = wreplsrv_find_owner(service, service->table, owner_in->address);
 
@@ -241,9 +262,8 @@ static NTSTATUS wreplsrv_in_send_request(struct wreplsrv_in_call *call)
                                 (long long)owner_in->min_version, 
                                 (long long)owner_in->max_version);
        NT_STATUS_HAVE_NO_MEMORY(filter);
-       ret = ldb_search(service->wins_db->ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &res);
+       ret = ldb_search(service->wins_db->ldb, call, &res, NULL, LDB_SCOPE_SUBTREE, NULL, "%s", filter);
        if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
-       talloc_steal(call, res);
        DEBUG(10,("WINSREPL: filter '%s' count %d\n", filter, res->count));
 
        if (res->count == 0) {
@@ -258,26 +278,37 @@ static NTSTATUS wreplsrv_in_send_request(struct wreplsrv_in_call *call)
        names = talloc_array(call, struct wrepl_wins_name, res->count);
        NT_STATUS_HAVE_NO_MEMORY(names);
 
-       for (i = 0; i < res->count; i++) {
-               status = winsdb_record(service->wins_db, res->msgs[i], call, &rec);
+       for (i=0, j=0; i < res->count; i++) {
+               status = winsdb_record(service->wins_db, res->msgs[i], call, now, &rec);
                NT_STATUS_NOT_OK_RETURN(status);
 
-               status = wreplsrv_record2wins_name(names, &names[i], rec);
-               NT_STATUS_NOT_OK_RETURN(status);
+               /*
+                * it's possible that winsdb_record() made the record RELEASED
+                * because it's expired, but in the database it's still stored
+                * as ACTIVE...
+                *
+                * make sure we really only replicate ACTIVE and TOMBSTONE records
+                */
+               if (rec->state == WREPL_STATE_ACTIVE || rec->state == WREPL_STATE_TOMBSTONE) {
+                       status = wreplsrv_record2wins_name(names, &names[j], rec);
+                       NT_STATUS_NOT_OK_RETURN(status);
+                       j++;
+               }
+
                talloc_free(rec);
                talloc_free(res->msgs[i]);
        }
 
        /* sort the names before we send them */
-       qsort(names, res->count, sizeof(struct wrepl_wins_name), (comparison_fn_t)wreplsrv_in_sort_wins_name);
+       qsort(names, j, sizeof(struct wrepl_wins_name), (comparison_fn_t)wreplsrv_in_sort_wins_name);
 
        DEBUG(2,("WINSREPL:reply [%u] records owner[%s] min[%llu] max[%llu] to partner[%s]\n",
-               res->count, owner_in->address, 
+               j, owner_in->address, 
                (long long)owner_in->min_version, 
                (long long)owner_in->max_version,
                call->wreplconn->partner->address));
 
-       reply_out->num_names    = res->count;
+       reply_out->num_names    = j;
        reply_out->names        = names;
 
        return NT_STATUS_OK;
@@ -401,14 +432,21 @@ static NTSTATUS wreplsrv_in_replication(struct wreplsrv_in_call *call)
        }
 
        if (!call->wreplconn->partner) {
-               DEBUG(1,("Failing WINS replication from non-partner %s\n",
-                        socket_get_peer_addr(call->wreplconn->conn->socket, call)));
-               return wreplsrv_in_stop_assoc_ctx(call);
+               struct socket_address *partner_ip = socket_get_peer_addr(call->wreplconn->conn->socket, call);
+
+               call->wreplconn->partner = wreplsrv_find_partner(call->wreplconn->service, partner_ip->addr);
+               if (!call->wreplconn->partner) {
+                       DEBUG(1,("Failing WINS replication from non-partner %s\n",
+                                partner_ip ? partner_ip->addr : NULL));
+                       return wreplsrv_in_stop_assoc_ctx(call);
+               }
        }
 
        switch (repl_in->command) {
                case WREPL_REPL_TABLE_QUERY:
                        if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PUSH)) {
+                               DEBUG(2,("Failing WINS replication TABLE_QUERY from non-push-partner %s\n",
+                                        call->wreplconn->partner->address));
                                return wreplsrv_in_stop_assoc_ctx(call);
                        }
                        status = wreplsrv_in_table_query(call);
@@ -419,6 +457,8 @@ static NTSTATUS wreplsrv_in_replication(struct wreplsrv_in_call *call)
 
                case WREPL_REPL_SEND_REQUEST:
                        if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PUSH)) {
+                               DEBUG(2,("Failing WINS replication SEND_REQUESET from non-push-partner %s\n",
+                                        call->wreplconn->partner->address));
                                return wreplsrv_in_stop_assoc_ctx(call);
                        }
                        status = wreplsrv_in_send_request(call);
@@ -429,6 +469,8 @@ static NTSTATUS wreplsrv_in_replication(struct wreplsrv_in_call *call)
        
                case WREPL_REPL_UPDATE:
                        if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
+                               DEBUG(2,("Failing WINS replication UPDATE from non-pull-partner %s\n",
+                                        call->wreplconn->partner->address));
                                return wreplsrv_in_stop_assoc_ctx(call);
                        }
                        status = wreplsrv_in_update(call);
@@ -436,6 +478,8 @@ static NTSTATUS wreplsrv_in_replication(struct wreplsrv_in_call *call)
 
                case WREPL_REPL_UPDATE2:
                        if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
+                               DEBUG(2,("Failing WINS replication UPDATE2 from non-pull-partner %s\n",
+                                        call->wreplconn->partner->address));
                                return wreplsrv_in_stop_assoc_ctx(call);
                        }
                        status = wreplsrv_in_update2(call);
@@ -443,6 +487,8 @@ static NTSTATUS wreplsrv_in_replication(struct wreplsrv_in_call *call)
 
                case WREPL_REPL_INFORM:
                        if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
+                               DEBUG(2,("Failing WINS replication INFORM from non-pull-partner %s\n",
+                                        call->wreplconn->partner->address));
                                return wreplsrv_in_stop_assoc_ctx(call);
                        }
                        status = wreplsrv_in_inform(call);
@@ -450,6 +496,8 @@ static NTSTATUS wreplsrv_in_replication(struct wreplsrv_in_call *call)
 
                case WREPL_REPL_INFORM2:
                        if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
+                               DEBUG(2,("Failing WINS replication INFORM2 from non-pull-partner %s\n",
+                                        call->wreplconn->partner->address));
                                return wreplsrv_in_stop_assoc_ctx(call);
                        }
                        status = wreplsrv_in_inform2(call);