r20646: first preparations for cluster enablement. This changes "
[samba.git] / source / wrepl_server / wrepl_in_call.c
1 /* 
2    Unix SMB/CIFS implementation.
3    
4    WINS Replication server
5    
6    Copyright (C) Stefan Metzmacher      2005
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24 #include "lib/events/events.h"
25 #include "lib/socket/socket.h"
26 #include "smbd/service_stream.h"
27 #include "libcli/wrepl/winsrepl.h"
28 #include "wrepl_server/wrepl_server.h"
29 #include "libcli/composite/composite.h"
30 #include "nbt_server/wins/winsdb.h"
31 #include "lib/ldb/include/ldb.h"
32 #include "lib/ldb/include/ldb_errors.h"
33 #include "system/time.h"
34
35 static NTSTATUS wreplsrv_in_start_association(struct wreplsrv_in_call *call)
36 {
37         struct wrepl_start *start       = &call->req_packet.message.start;
38         struct wrepl_start *start_reply = &call->rep_packet.message.start_reply;
39
40         if (call->req_packet.opcode & WREPL_OPCODE_BITS) {
41                 /*
42                  *if the assoc_ctx doesn't match ignore the packet
43                  */
44                 if ((call->req_packet.assoc_ctx != call->wreplconn->assoc_ctx.our_ctx)
45                    && (call->req_packet.assoc_ctx != 0)) {
46                         return ERROR_INVALID_PARAMETER;
47                 }
48         } else {
49                 call->wreplconn->assoc_ctx.our_ctx = WREPLSRV_INVALID_ASSOC_CTX;
50                 return NT_STATUS_OK;
51         }
52
53 /*
54  * it seems that we don't know all details about the start_association
55  * to support replication with NT4 (it sends 1.1 instead of 5.2)
56  * we ignore the version numbers until we know all details
57  */
58 #if 0
59         if (start->minor_version != 2 || start->major_version != 5) {
60                 /* w2k terminate the connection if the versions doesn't match */
61                 return NT_STATUS_UNKNOWN_REVISION;
62         }
63 #endif
64
65         call->wreplconn->assoc_ctx.stopped      = False;
66         call->wreplconn->assoc_ctx.our_ctx      = WREPLSRV_VALID_ASSOC_CTX;
67         call->wreplconn->assoc_ctx.peer_ctx     = start->assoc_ctx;
68
69         call->rep_packet.mess_type              = WREPL_START_ASSOCIATION_REPLY;
70         start_reply->assoc_ctx                  = call->wreplconn->assoc_ctx.our_ctx;
71         start_reply->minor_version              = 2;
72         start_reply->major_version              = 5;
73
74         /*
75          * nt4 uses 41 bytes for the start_association call
76          * so do it the same and as we don't know the meanings of this bytes
77          * we just send zeros and nt4, w2k and w2k3 seems to be happy with this
78          *
79          * if we don't do this nt4 uses an old version of the wins replication protocol
80          * and that would break nt4 <-> samba replication
81          */
82         call->rep_packet.padding                = data_blob_talloc(call, NULL, 21);
83         NT_STATUS_HAVE_NO_MEMORY(call->rep_packet.padding.data);
84
85         memset(call->rep_packet.padding.data, 0, call->rep_packet.padding.length);
86
87         return NT_STATUS_OK;
88 }
89
90 static NTSTATUS wreplsrv_in_stop_assoc_ctx(struct wreplsrv_in_call *call)
91 {
92         struct wrepl_stop *stop_out             = &call->rep_packet.message.stop;
93
94         call->wreplconn->assoc_ctx.stopped      = True;
95
96         call->rep_packet.mess_type              = WREPL_STOP_ASSOCIATION;
97         stop_out->reason                        = 4;
98
99         return NT_STATUS_OK;
100 }
101
102 static NTSTATUS wreplsrv_in_stop_association(struct wreplsrv_in_call *call)
103 {
104         /*
105          * w2k only check the assoc_ctx if the opcode has the 0x00007800 bits are set
106          */
107         if (call->req_packet.opcode & WREPL_OPCODE_BITS) {
108                 /*
109                  *if the assoc_ctx doesn't match ignore the packet
110                  */
111                 if (call->req_packet.assoc_ctx != call->wreplconn->assoc_ctx.our_ctx) {
112                         return ERROR_INVALID_PARAMETER;
113                 }
114                 /* when the opcode bits are set the connection should be directly terminated */
115                 return NT_STATUS_CONNECTION_RESET;
116         }
117
118         if (call->wreplconn->assoc_ctx.stopped) {
119                 /* this causes the connection to be directly terminated */
120                 return NT_STATUS_CONNECTION_RESET;
121         }
122
123         /* this will cause to not receive packets anymore and terminate the connection if the reply is send */
124         call->terminate_after_send = True;
125         return wreplsrv_in_stop_assoc_ctx(call);
126 }
127
128 static NTSTATUS wreplsrv_in_table_query(struct wreplsrv_in_call *call)
129 {
130         struct wreplsrv_service *service = call->wreplconn->service;
131         struct wrepl_replication *repl_out = &call->rep_packet.message.replication;
132         struct wrepl_table *table_out = &call->rep_packet.message.replication.info.table;
133
134         repl_out->command = WREPL_REPL_TABLE_REPLY;
135
136         return wreplsrv_fill_wrepl_table(service, call, table_out,
137                                          service->wins_db->local_owner, True);
138 }
139
140 static int wreplsrv_in_sort_wins_name(struct wrepl_wins_name *n1,
141                                       struct wrepl_wins_name *n2)
142 {
143         if (n1->id < n2->id) return -1;
144         if (n1->id > n2->id) return 1;
145         return 0;
146 }
147
148 static NTSTATUS wreplsrv_record2wins_name(TALLOC_CTX *mem_ctx,
149                                           struct wrepl_wins_name *name,
150                                           struct winsdb_record *rec)
151 {
152         uint32_t num_ips, i;
153         struct wrepl_ip *ips;
154
155         name->name              = rec->name;
156         talloc_steal(mem_ctx, rec->name);
157
158         name->id                = rec->version;
159         name->unknown           = "255.255.255.255";
160
161         name->flags             = WREPL_NAME_FLAGS(rec->type, rec->state, rec->node, rec->is_static);
162
163         switch (name->flags & 2) {
164         case 0:
165                 name->addresses.ip                      = rec->addresses[0]->address;
166                 talloc_steal(mem_ctx, rec->addresses[0]->address);
167                 break;
168         case 2:
169                 num_ips = winsdb_addr_list_length(rec->addresses);
170                 ips     = talloc_array(mem_ctx, struct wrepl_ip, num_ips);
171                 NT_STATUS_HAVE_NO_MEMORY(ips);
172
173                 for (i = 0; i < num_ips; i++) {
174                         ips[i].owner    = rec->addresses[i]->wins_owner;
175                         talloc_steal(ips, rec->addresses[i]->wins_owner);
176                         ips[i].ip       = rec->addresses[i]->address;
177                         talloc_steal(ips, rec->addresses[i]->address);
178                 }
179
180                 name->addresses.addresses.num_ips       = num_ips;
181                 name->addresses.addresses.ips           = ips;
182                 break;
183         }
184
185         return NT_STATUS_OK;
186 }
187
188 static NTSTATUS wreplsrv_in_send_request(struct wreplsrv_in_call *call)
189 {
190         struct wreplsrv_service *service = call->wreplconn->service;
191         struct wrepl_wins_owner *owner_in = &call->req_packet.message.replication.info.owner;
192         struct wrepl_replication *repl_out = &call->rep_packet.message.replication;
193         struct wrepl_send_reply *reply_out = &call->rep_packet.message.replication.info.reply;
194         struct wreplsrv_owner *owner;
195         const char *owner_filter;
196         const char *filter;
197         struct ldb_result *res = NULL;
198         int ret;
199         struct wrepl_wins_name *names;
200         struct winsdb_record *rec;
201         NTSTATUS status;
202         uint32_t i, j;
203         time_t now = time(NULL);
204
205         owner = wreplsrv_find_owner(service, service->table, owner_in->address);
206
207         repl_out->command       = WREPL_REPL_SEND_REPLY;
208         reply_out->num_names    = 0;
209         reply_out->names        = NULL;
210
211         /*
212          * if we didn't know this owner, must be a bug in the partners client code...
213          * return an empty list.
214          */
215         if (!owner) {
216                 DEBUG(2,("WINSREPL:reply [0] records unknown owner[%s] to partner[%s]\n",
217                         owner_in->address, call->wreplconn->partner->address));
218                 return NT_STATUS_OK;
219         }
220
221         /*
222          * the client sends a max_version of 0, interpret it as
223          * (uint64_t)-1
224          */
225         if (owner_in->max_version == 0) {
226                 owner_in->max_version = (uint64_t)-1;
227         }
228
229         /*
230          * if the partner ask for nothing, or give invalid ranges,
231          * return an empty list.
232          */
233         if (owner_in->min_version > owner_in->max_version) {
234                 DEBUG(2,("WINSREPL:reply [0] records owner[%s] min[%llu] max[%llu] to partner[%s]\n",
235                         owner_in->address, 
236                         (long long)owner_in->min_version, 
237                         (long long)owner_in->max_version,
238                         call->wreplconn->partner->address));
239                 return NT_STATUS_OK;
240         }
241
242         /*
243          * if the partner has already all records for nothing, or give invalid ranges,
244          * return an empty list.
245          */
246         if (owner_in->min_version > owner->owner.max_version) {
247                 DEBUG(2,("WINSREPL:reply [0] records owner[%s] min[%llu] max[%llu] to partner[%s]\n",
248                         owner_in->address, 
249                         (long long)owner_in->min_version, 
250                         (long long)owner_in->max_version,
251                         call->wreplconn->partner->address));
252                 return NT_STATUS_OK;
253         }
254
255         owner_filter = wreplsrv_owner_filter(service, call, owner->owner.address);
256         NT_STATUS_HAVE_NO_MEMORY(owner_filter);
257         filter = talloc_asprintf(call,
258                                  "(&%s(objectClass=winsRecord)"
259                                  "(|(recordState=%u)(recordState=%u))"
260                                  "(versionID>=%llu)(versionID<=%llu))",
261                                  owner_filter,
262                                  WREPL_STATE_ACTIVE, WREPL_STATE_TOMBSTONE,
263                                  (long long)owner_in->min_version, 
264                                  (long long)owner_in->max_version);
265         NT_STATUS_HAVE_NO_MEMORY(filter);
266         ret = ldb_search(service->wins_db->ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &res);
267         if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
268         talloc_steal(call, res);
269         DEBUG(10,("WINSREPL: filter '%s' count %d\n", filter, res->count));
270
271         if (res->count == 0) {
272                 DEBUG(2,("WINSREPL:reply [%u] records owner[%s] min[%llu] max[%llu] to partner[%s]\n",
273                         res->count, owner_in->address, 
274                         (long long)owner_in->min_version, 
275                         (long long)owner_in->max_version,
276                         call->wreplconn->partner->address));
277                 return NT_STATUS_OK;
278         }
279
280         names = talloc_array(call, struct wrepl_wins_name, res->count);
281         NT_STATUS_HAVE_NO_MEMORY(names);
282
283         for (i=0, j=0; i < res->count; i++) {
284                 status = winsdb_record(service->wins_db, res->msgs[i], call, now, &rec);
285                 NT_STATUS_NOT_OK_RETURN(status);
286
287                 /*
288                  * it's possible that winsdb_record() made the record RELEASED
289                  * because it's expired, but in the database it's still stored
290                  * as ACTIVE...
291                  *
292                  * make sure we really only replicate ACTIVE and TOMBSTONE records
293                  */
294                 if (rec->state == WREPL_STATE_ACTIVE || rec->state == WREPL_STATE_TOMBSTONE) {
295                         status = wreplsrv_record2wins_name(names, &names[j], rec);
296                         NT_STATUS_NOT_OK_RETURN(status);
297                         j++;
298                 }
299
300                 talloc_free(rec);
301                 talloc_free(res->msgs[i]);
302         }
303
304         /* sort the names before we send them */
305         qsort(names, j, sizeof(struct wrepl_wins_name), (comparison_fn_t)wreplsrv_in_sort_wins_name);
306
307         DEBUG(2,("WINSREPL:reply [%u] records owner[%s] min[%llu] max[%llu] to partner[%s]\n",
308                 j, owner_in->address, 
309                 (long long)owner_in->min_version, 
310                 (long long)owner_in->max_version,
311                 call->wreplconn->partner->address));
312
313         reply_out->num_names    = j;
314         reply_out->names        = names;
315
316         return NT_STATUS_OK;
317 }
318
319 struct wreplsrv_in_update_state {
320         struct wreplsrv_in_connection *wrepl_in;
321         struct wreplsrv_out_connection *wrepl_out;
322         struct composite_context *creq;
323         struct wreplsrv_pull_cycle_io cycle_io;
324 };
325
326 static void wreplsrv_in_update_handler(struct composite_context *creq)
327 {
328         struct wreplsrv_in_update_state *update_state = talloc_get_type(creq->async.private_data,
329                                                         struct wreplsrv_in_update_state);
330         NTSTATUS status;
331
332         status = wreplsrv_pull_cycle_recv(creq);
333
334         talloc_free(update_state->wrepl_out);
335
336         wreplsrv_terminate_in_connection(update_state->wrepl_in, nt_errstr(status));
337 }
338
339 static NTSTATUS wreplsrv_in_update(struct wreplsrv_in_call *call)
340 {
341         struct wreplsrv_in_connection *wrepl_in = call->wreplconn;
342         struct wreplsrv_out_connection *wrepl_out;
343         struct wrepl_table *update_in = &call->req_packet.message.replication.info.table;
344         struct wreplsrv_in_update_state *update_state;
345         uint16_t fde_flags;
346
347         DEBUG(2,("WREPL_REPL_UPDATE: partner[%s] initiator[%s] num_owners[%u]\n",
348                 call->wreplconn->partner->address,
349                 update_in->initiator, update_in->partner_count));
350
351         /* 
352          * we need to flip the connection into a client connection
353          * and do a WREPL_REPL_SEND_REQUEST's on the that connection
354          * and then stop this connection
355          */
356         fde_flags = event_get_fd_flags(wrepl_in->conn->event.fde);
357         talloc_free(wrepl_in->conn->event.fde);
358         wrepl_in->conn->event.fde = NULL;
359
360         update_state = talloc(wrepl_in, struct wreplsrv_in_update_state);
361         NT_STATUS_HAVE_NO_MEMORY(update_state);
362
363         wrepl_out = talloc(update_state, struct wreplsrv_out_connection);
364         NT_STATUS_HAVE_NO_MEMORY(wrepl_out);
365         wrepl_out->service              = wrepl_in->service;
366         wrepl_out->partner              = wrepl_in->partner;
367         wrepl_out->assoc_ctx.our_ctx    = wrepl_in->assoc_ctx.our_ctx;
368         wrepl_out->assoc_ctx.peer_ctx   = wrepl_in->assoc_ctx.peer_ctx;
369         wrepl_out->sock                 = wrepl_socket_merge(wrepl_out,
370                                                              wrepl_in->conn->event.ctx,
371                                                              wrepl_in->conn->socket,
372                                                              wrepl_in->packet);
373         NT_STATUS_HAVE_NO_MEMORY(wrepl_out->sock);
374
375         event_set_fd_flags(wrepl_out->sock->event.fde, fde_flags);
376
377         update_state->wrepl_in                  = wrepl_in;
378         update_state->wrepl_out                 = wrepl_out;
379         update_state->cycle_io.in.partner       = wrepl_out->partner;
380         update_state->cycle_io.in.num_owners    = update_in->partner_count;
381         update_state->cycle_io.in.owners        = update_in->partners;
382         talloc_steal(update_state, update_in->partners);
383         update_state->cycle_io.in.wreplconn     = wrepl_out;
384         update_state->creq = wreplsrv_pull_cycle_send(update_state, &update_state->cycle_io);
385         if (!update_state->creq) {
386                 return NT_STATUS_INTERNAL_ERROR;
387         }
388
389         update_state->creq->async.fn            = wreplsrv_in_update_handler;
390         update_state->creq->async.private_data  = update_state;
391
392         return ERROR_INVALID_PARAMETER;
393 }
394
395 static NTSTATUS wreplsrv_in_update2(struct wreplsrv_in_call *call)
396 {
397         return wreplsrv_in_update(call);
398 }
399
400 static NTSTATUS wreplsrv_in_inform(struct wreplsrv_in_call *call)
401 {
402         struct wrepl_table *inform_in = &call->req_packet.message.replication.info.table;
403
404         DEBUG(2,("WREPL_REPL_INFORM: partner[%s] initiator[%s] num_owners[%u]\n",
405                 call->wreplconn->partner->address,
406                 inform_in->initiator, inform_in->partner_count));
407
408         wreplsrv_out_partner_pull(call->wreplconn->partner, inform_in);
409
410         /* we don't reply to WREPL_REPL_INFORM messages */
411         return ERROR_INVALID_PARAMETER;
412 }
413
414 static NTSTATUS wreplsrv_in_inform2(struct wreplsrv_in_call *call)
415 {
416         return wreplsrv_in_inform(call);
417 }
418
419 static NTSTATUS wreplsrv_in_replication(struct wreplsrv_in_call *call)
420 {
421         struct wrepl_replication *repl_in = &call->req_packet.message.replication;
422         NTSTATUS status;
423
424         /*
425          * w2k only check the assoc_ctx if the opcode has the 0x00007800 bits are set
426          */
427         if (call->req_packet.opcode & WREPL_OPCODE_BITS) {
428                 /*
429                  *if the assoc_ctx doesn't match ignore the packet
430                  */
431                 if (call->req_packet.assoc_ctx != call->wreplconn->assoc_ctx.our_ctx) {
432                         return ERROR_INVALID_PARAMETER;
433                 }
434         }
435
436         if (!call->wreplconn->partner) {
437                 struct socket_address *partner_ip = socket_get_peer_addr(call->wreplconn->conn->socket, call);
438
439                 call->wreplconn->partner = wreplsrv_find_partner(call->wreplconn->service, partner_ip->addr);
440                 if (!call->wreplconn->partner) {
441                         DEBUG(1,("Failing WINS replication from non-partner %s\n",
442                                  partner_ip ? partner_ip->addr : NULL));
443                         return wreplsrv_in_stop_assoc_ctx(call);
444                 }
445         }
446
447         switch (repl_in->command) {
448                 case WREPL_REPL_TABLE_QUERY:
449                         if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PUSH)) {
450                                 DEBUG(2,("Failing WINS replication TABLE_QUERY from non-push-partner %s\n",
451                                          call->wreplconn->partner->address));
452                                 return wreplsrv_in_stop_assoc_ctx(call);
453                         }
454                         status = wreplsrv_in_table_query(call);
455                         break;
456
457                 case WREPL_REPL_TABLE_REPLY:
458                         return ERROR_INVALID_PARAMETER;
459
460                 case WREPL_REPL_SEND_REQUEST:
461                         if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PUSH)) {
462                                 DEBUG(2,("Failing WINS replication SEND_REQUESET from non-push-partner %s\n",
463                                          call->wreplconn->partner->address));
464                                 return wreplsrv_in_stop_assoc_ctx(call);
465                         }
466                         status = wreplsrv_in_send_request(call);
467                         break;
468
469                 case WREPL_REPL_SEND_REPLY:
470                         return ERROR_INVALID_PARAMETER;
471         
472                 case WREPL_REPL_UPDATE:
473                         if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
474                                 DEBUG(2,("Failing WINS replication UPDATE from non-pull-partner %s\n",
475                                          call->wreplconn->partner->address));
476                                 return wreplsrv_in_stop_assoc_ctx(call);
477                         }
478                         status = wreplsrv_in_update(call);
479                         break;
480
481                 case WREPL_REPL_UPDATE2:
482                         if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
483                                 DEBUG(2,("Failing WINS replication UPDATE2 from non-pull-partner %s\n",
484                                          call->wreplconn->partner->address));
485                                 return wreplsrv_in_stop_assoc_ctx(call);
486                         }
487                         status = wreplsrv_in_update2(call);
488                         break;
489
490                 case WREPL_REPL_INFORM:
491                         if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
492                                 DEBUG(2,("Failing WINS replication INFORM from non-pull-partner %s\n",
493                                          call->wreplconn->partner->address));
494                                 return wreplsrv_in_stop_assoc_ctx(call);
495                         }
496                         status = wreplsrv_in_inform(call);
497                         break;
498
499                 case WREPL_REPL_INFORM2:
500                         if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
501                                 DEBUG(2,("Failing WINS replication INFORM2 from non-pull-partner %s\n",
502                                          call->wreplconn->partner->address));
503                                 return wreplsrv_in_stop_assoc_ctx(call);
504                         }
505                         status = wreplsrv_in_inform2(call);
506                         break;
507
508                 default:
509                         return ERROR_INVALID_PARAMETER;
510         }
511
512         if (NT_STATUS_IS_OK(status)) {
513                 call->rep_packet.mess_type = WREPL_REPLICATION;
514         }
515
516         return status;
517 }
518
519 static NTSTATUS wreplsrv_in_invalid_assoc_ctx(struct wreplsrv_in_call *call)
520 {
521         struct wrepl_start *start       = &call->rep_packet.message.start;
522
523         call->rep_packet.opcode         = 0x00008583;
524         call->rep_packet.assoc_ctx      = 0;
525         call->rep_packet.mess_type      = WREPL_START_ASSOCIATION;
526
527         start->assoc_ctx                = 0x0000000a;
528         start->minor_version            = 0x0001;
529         start->major_version            = 0x0000;
530
531         call->rep_packet.padding        = data_blob_talloc(call, NULL, 4);
532         memset(call->rep_packet.padding.data, '\0', call->rep_packet.padding.length);
533
534         return NT_STATUS_OK;
535 }
536
537 NTSTATUS wreplsrv_in_call(struct wreplsrv_in_call *call)
538 {
539         NTSTATUS status;
540
541         if (!(call->req_packet.opcode & WREPL_OPCODE_BITS)
542             && (call->wreplconn->assoc_ctx.our_ctx == WREPLSRV_INVALID_ASSOC_CTX)) {
543                 return wreplsrv_in_invalid_assoc_ctx(call);
544         }
545
546         switch (call->req_packet.mess_type) {
547                 case WREPL_START_ASSOCIATION:
548                         status = wreplsrv_in_start_association(call);
549                         break;
550                 case WREPL_START_ASSOCIATION_REPLY:
551                         /* this is not valid here, so we ignore it */
552                         return ERROR_INVALID_PARAMETER;
553
554                 case WREPL_STOP_ASSOCIATION:
555                         status = wreplsrv_in_stop_association(call);
556                         break;
557
558                 case WREPL_REPLICATION:
559                         status = wreplsrv_in_replication(call);
560                         break;
561                 default:
562                         /* everythingelse is also not valid here, so we ignore it */
563                         return ERROR_INVALID_PARAMETER;
564         }
565
566         if (call->wreplconn->assoc_ctx.our_ctx == WREPLSRV_INVALID_ASSOC_CTX) {
567                 return wreplsrv_in_invalid_assoc_ctx(call);
568         }
569
570         if (NT_STATUS_IS_OK(status)) {
571                 /* let the backend to set some of the opcode bits, but always add the standards */
572                 call->rep_packet.opcode         |= WREPL_OPCODE_BITS;
573                 call->rep_packet.assoc_ctx      = call->wreplconn->assoc_ctx.peer_ctx;
574         }
575
576         return status;
577 }