s4-wrepl: use TYPESAFE_QSORT() in wins repl code
[Samba/gebeck_regimport.git] / source4 / wrepl_server / wrepl_in_call.c
blobfd09bbaf4011359e338d5925beb379b0f586d934
1 /*
2 Unix SMB/CIFS implementation.
4 WINS Replication server
6 Copyright (C) Stefan Metzmacher 2005
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 3 of the License, or
11 (at your option) any later version.
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.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "includes.h"
23 #include "lib/events/events.h"
24 #include "lib/socket/socket.h"
25 #include "smbd/service_stream.h"
26 #include "libcli/wrepl/winsrepl.h"
27 #include "wrepl_server/wrepl_server.h"
28 #include "libcli/composite/composite.h"
29 #include "nbt_server/wins/winsdb.h"
30 #include "lib/ldb/include/ldb.h"
31 #include "lib/ldb/include/ldb_errors.h"
32 #include "system/time.h"
33 #include "lib/util/tsort.h"
35 static NTSTATUS wreplsrv_in_start_association(struct wreplsrv_in_call *call)
37 struct wrepl_start *start = &call->req_packet.message.start;
38 struct wrepl_start *start_reply = &call->rep_packet.message.start_reply;
40 if (call->req_packet.opcode & WREPL_OPCODE_BITS) {
42 *if the assoc_ctx doesn't match ignore the packet
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;
48 } else {
49 call->wreplconn->assoc_ctx.our_ctx = WREPLSRV_INVALID_ASSOC_CTX;
50 return NT_STATUS_OK;
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
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;
63 #endif
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;
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;
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
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
82 call->rep_packet.padding = data_blob_talloc(call, NULL, 21);
83 NT_STATUS_HAVE_NO_MEMORY(call->rep_packet.padding.data);
85 memset(call->rep_packet.padding.data, 0, call->rep_packet.padding.length);
87 return NT_STATUS_OK;
90 static NTSTATUS wreplsrv_in_stop_assoc_ctx(struct wreplsrv_in_call *call)
92 struct wrepl_stop *stop_out = &call->rep_packet.message.stop;
94 call->wreplconn->assoc_ctx.stopped = true;
96 call->rep_packet.mess_type = WREPL_STOP_ASSOCIATION;
97 stop_out->reason = 4;
99 return NT_STATUS_OK;
102 static NTSTATUS wreplsrv_in_stop_association(struct wreplsrv_in_call *call)
105 * w2k only check the assoc_ctx if the opcode has the 0x00007800 bits are set
107 if (call->req_packet.opcode & WREPL_OPCODE_BITS) {
109 *if the assoc_ctx doesn't match ignore the packet
111 if (call->req_packet.assoc_ctx != call->wreplconn->assoc_ctx.our_ctx) {
112 return ERROR_INVALID_PARAMETER;
114 /* when the opcode bits are set the connection should be directly terminated */
115 return NT_STATUS_CONNECTION_RESET;
118 if (call->wreplconn->assoc_ctx.stopped) {
119 /* this causes the connection to be directly terminated */
120 return NT_STATUS_CONNECTION_RESET;
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);
128 static NTSTATUS wreplsrv_in_table_query(struct wreplsrv_in_call *call)
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;
134 repl_out->command = WREPL_REPL_TABLE_REPLY;
136 return wreplsrv_fill_wrepl_table(service, call, table_out,
137 service->wins_db->local_owner, true);
140 static int wreplsrv_in_sort_wins_name(struct wrepl_wins_name *n1,
141 struct wrepl_wins_name *n2)
143 if (n1->id < n2->id) return -1;
144 if (n1->id > n2->id) return 1;
145 return 0;
148 static NTSTATUS wreplsrv_record2wins_name(TALLOC_CTX *mem_ctx,
149 struct wrepl_wins_name *name,
150 struct winsdb_record *rec)
152 uint32_t num_ips, i;
153 struct wrepl_ip *ips;
155 name->name = rec->name;
156 talloc_steal(mem_ctx, rec->name);
158 name->id = rec->version;
159 name->unknown = "255.255.255.255";
161 name->flags = WREPL_NAME_FLAGS(rec->type, rec->state, rec->node, rec->is_static);
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);
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);
180 name->addresses.addresses.num_ips = num_ips;
181 name->addresses.addresses.ips = ips;
182 break;
185 return NT_STATUS_OK;
188 static NTSTATUS wreplsrv_in_send_request(struct wreplsrv_in_call *call)
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);
205 owner = wreplsrv_find_owner(service, service->table, owner_in->address);
207 repl_out->command = WREPL_REPL_SEND_REPLY;
208 reply_out->num_names = 0;
209 reply_out->names = NULL;
212 * if we didn't know this owner, must be a bug in the partners client code...
213 * return an empty list.
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;
222 * the client sends a max_version of 0, interpret it as
223 * (uint64_t)-1
225 if (owner_in->max_version == 0) {
226 owner_in->max_version = (uint64_t)-1;
230 * if the partner ask for nothing, or give invalid ranges,
231 * return an empty list.
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;
243 * if the partner has already all records for nothing, or give invalid ranges,
244 * return an empty list.
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;
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, call, &res, NULL, LDB_SCOPE_SUBTREE, NULL, "%s", filter);
267 if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
268 DEBUG(10,("WINSREPL: filter '%s' count %d\n", filter, res->count));
270 if (res->count == 0) {
271 DEBUG(2,("WINSREPL:reply [%u] records owner[%s] min[%llu] max[%llu] to partner[%s]\n",
272 res->count, owner_in->address,
273 (long long)owner_in->min_version,
274 (long long)owner_in->max_version,
275 call->wreplconn->partner->address));
276 return NT_STATUS_OK;
279 names = talloc_array(call, struct wrepl_wins_name, res->count);
280 NT_STATUS_HAVE_NO_MEMORY(names);
282 for (i=0, j=0; i < res->count; i++) {
283 status = winsdb_record(service->wins_db, res->msgs[i], call, now, &rec);
284 NT_STATUS_NOT_OK_RETURN(status);
287 * it's possible that winsdb_record() made the record RELEASED
288 * because it's expired, but in the database it's still stored
289 * as ACTIVE...
291 * make sure we really only replicate ACTIVE and TOMBSTONE records
293 if (rec->state == WREPL_STATE_ACTIVE || rec->state == WREPL_STATE_TOMBSTONE) {
294 status = wreplsrv_record2wins_name(names, &names[j], rec);
295 NT_STATUS_NOT_OK_RETURN(status);
296 j++;
299 talloc_free(rec);
300 talloc_free(res->msgs[i]);
303 /* sort the names before we send them */
304 TYPESAFE_QSORT(names, j, wreplsrv_in_sort_wins_name);
306 DEBUG(2,("WINSREPL:reply [%u] records owner[%s] min[%llu] max[%llu] to partner[%s]\n",
307 j, owner_in->address,
308 (long long)owner_in->min_version,
309 (long long)owner_in->max_version,
310 call->wreplconn->partner->address));
312 reply_out->num_names = j;
313 reply_out->names = names;
315 return NT_STATUS_OK;
318 struct wreplsrv_in_update_state {
319 struct wreplsrv_in_connection *wrepl_in;
320 struct wreplsrv_out_connection *wrepl_out;
321 struct composite_context *creq;
322 struct wreplsrv_pull_cycle_io cycle_io;
325 static void wreplsrv_in_update_handler(struct composite_context *creq)
327 struct wreplsrv_in_update_state *update_state = talloc_get_type(creq->async.private_data,
328 struct wreplsrv_in_update_state);
329 NTSTATUS status;
331 status = wreplsrv_pull_cycle_recv(creq);
333 talloc_free(update_state->wrepl_out);
335 wreplsrv_terminate_in_connection(update_state->wrepl_in, nt_errstr(status));
338 static NTSTATUS wreplsrv_in_update(struct wreplsrv_in_call *call)
340 struct wreplsrv_in_connection *wrepl_in = call->wreplconn;
341 struct wreplsrv_out_connection *wrepl_out;
342 struct wrepl_table *update_in = &call->req_packet.message.replication.info.table;
343 struct wreplsrv_in_update_state *update_state;
344 uint16_t fde_flags;
346 DEBUG(2,("WREPL_REPL_UPDATE: partner[%s] initiator[%s] num_owners[%u]\n",
347 call->wreplconn->partner->address,
348 update_in->initiator, update_in->partner_count));
351 * we need to flip the connection into a client connection
352 * and do a WREPL_REPL_SEND_REQUEST's on the that connection
353 * and then stop this connection
355 fde_flags = event_get_fd_flags(wrepl_in->conn->event.fde);
356 talloc_free(wrepl_in->conn->event.fde);
357 wrepl_in->conn->event.fde = NULL;
359 update_state = talloc(wrepl_in, struct wreplsrv_in_update_state);
360 NT_STATUS_HAVE_NO_MEMORY(update_state);
362 wrepl_out = talloc(update_state, struct wreplsrv_out_connection);
363 NT_STATUS_HAVE_NO_MEMORY(wrepl_out);
364 wrepl_out->service = wrepl_in->service;
365 wrepl_out->partner = wrepl_in->partner;
366 wrepl_out->assoc_ctx.our_ctx = wrepl_in->assoc_ctx.our_ctx;
367 wrepl_out->assoc_ctx.peer_ctx = wrepl_in->assoc_ctx.peer_ctx;
368 wrepl_out->sock = wrepl_socket_merge(wrepl_out,
369 wrepl_in->conn->event.ctx,
370 wrepl_in->conn->socket,
371 wrepl_in->packet);
372 NT_STATUS_HAVE_NO_MEMORY(wrepl_out->sock);
374 event_set_fd_flags(wrepl_out->sock->event.fde, fde_flags);
376 update_state->wrepl_in = wrepl_in;
377 update_state->wrepl_out = wrepl_out;
378 update_state->cycle_io.in.partner = wrepl_out->partner;
379 update_state->cycle_io.in.num_owners = update_in->partner_count;
380 update_state->cycle_io.in.owners = update_in->partners;
381 talloc_steal(update_state, update_in->partners);
382 update_state->cycle_io.in.wreplconn = wrepl_out;
383 update_state->creq = wreplsrv_pull_cycle_send(update_state, &update_state->cycle_io);
384 if (!update_state->creq) {
385 return NT_STATUS_INTERNAL_ERROR;
388 update_state->creq->async.fn = wreplsrv_in_update_handler;
389 update_state->creq->async.private_data = update_state;
391 return ERROR_INVALID_PARAMETER;
394 static NTSTATUS wreplsrv_in_update2(struct wreplsrv_in_call *call)
396 return wreplsrv_in_update(call);
399 static NTSTATUS wreplsrv_in_inform(struct wreplsrv_in_call *call)
401 struct wrepl_table *inform_in = &call->req_packet.message.replication.info.table;
403 DEBUG(2,("WREPL_REPL_INFORM: partner[%s] initiator[%s] num_owners[%u]\n",
404 call->wreplconn->partner->address,
405 inform_in->initiator, inform_in->partner_count));
407 wreplsrv_out_partner_pull(call->wreplconn->partner, inform_in);
409 /* we don't reply to WREPL_REPL_INFORM messages */
410 return ERROR_INVALID_PARAMETER;
413 static NTSTATUS wreplsrv_in_inform2(struct wreplsrv_in_call *call)
415 return wreplsrv_in_inform(call);
418 static NTSTATUS wreplsrv_in_replication(struct wreplsrv_in_call *call)
420 struct wrepl_replication *repl_in = &call->req_packet.message.replication;
421 NTSTATUS status;
424 * w2k only check the assoc_ctx if the opcode has the 0x00007800 bits are set
426 if (call->req_packet.opcode & WREPL_OPCODE_BITS) {
428 *if the assoc_ctx doesn't match ignore the packet
430 if (call->req_packet.assoc_ctx != call->wreplconn->assoc_ctx.our_ctx) {
431 return ERROR_INVALID_PARAMETER;
435 if (!call->wreplconn->partner) {
436 struct socket_address *partner_ip = socket_get_peer_addr(call->wreplconn->conn->socket, call);
438 call->wreplconn->partner = wreplsrv_find_partner(call->wreplconn->service, partner_ip->addr);
439 if (!call->wreplconn->partner) {
440 DEBUG(1,("Failing WINS replication from non-partner %s\n",
441 partner_ip ? partner_ip->addr : NULL));
442 return wreplsrv_in_stop_assoc_ctx(call);
446 switch (repl_in->command) {
447 case WREPL_REPL_TABLE_QUERY:
448 if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PUSH)) {
449 DEBUG(0,("Failing WINS replication TABLE_QUERY from non-push-partner %s\n",
450 call->wreplconn->partner->address));
451 return wreplsrv_in_stop_assoc_ctx(call);
453 status = wreplsrv_in_table_query(call);
454 break;
456 case WREPL_REPL_TABLE_REPLY:
457 return ERROR_INVALID_PARAMETER;
459 case WREPL_REPL_SEND_REQUEST:
460 if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PUSH)) {
461 DEBUG(0,("Failing WINS replication SEND_REQUESET from non-push-partner %s\n",
462 call->wreplconn->partner->address));
463 return wreplsrv_in_stop_assoc_ctx(call);
465 status = wreplsrv_in_send_request(call);
466 break;
468 case WREPL_REPL_SEND_REPLY:
469 return ERROR_INVALID_PARAMETER;
471 case WREPL_REPL_UPDATE:
472 if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
473 DEBUG(0,("Failing WINS replication UPDATE from non-pull-partner %s\n",
474 call->wreplconn->partner->address));
475 return wreplsrv_in_stop_assoc_ctx(call);
477 status = wreplsrv_in_update(call);
478 break;
480 case WREPL_REPL_UPDATE2:
481 if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
482 DEBUG(0,("Failing WINS replication UPDATE2 from non-pull-partner %s\n",
483 call->wreplconn->partner->address));
484 return wreplsrv_in_stop_assoc_ctx(call);
486 status = wreplsrv_in_update2(call);
487 break;
489 case WREPL_REPL_INFORM:
490 if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
491 DEBUG(0,("Failing WINS replication INFORM from non-pull-partner %s\n",
492 call->wreplconn->partner->address));
493 return wreplsrv_in_stop_assoc_ctx(call);
495 status = wreplsrv_in_inform(call);
496 break;
498 case WREPL_REPL_INFORM2:
499 if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
500 DEBUG(0,("Failing WINS replication INFORM2 from non-pull-partner %s\n",
501 call->wreplconn->partner->address));
502 return wreplsrv_in_stop_assoc_ctx(call);
504 status = wreplsrv_in_inform2(call);
505 break;
507 default:
508 return ERROR_INVALID_PARAMETER;
511 if (NT_STATUS_IS_OK(status)) {
512 call->rep_packet.mess_type = WREPL_REPLICATION;
515 return status;
518 static NTSTATUS wreplsrv_in_invalid_assoc_ctx(struct wreplsrv_in_call *call)
520 struct wrepl_start *start = &call->rep_packet.message.start;
522 call->rep_packet.opcode = 0x00008583;
523 call->rep_packet.assoc_ctx = 0;
524 call->rep_packet.mess_type = WREPL_START_ASSOCIATION;
526 start->assoc_ctx = 0x0000000a;
527 start->minor_version = 0x0001;
528 start->major_version = 0x0000;
530 call->rep_packet.padding = data_blob_talloc(call, NULL, 4);
531 memset(call->rep_packet.padding.data, '\0', call->rep_packet.padding.length);
533 return NT_STATUS_OK;
536 NTSTATUS wreplsrv_in_call(struct wreplsrv_in_call *call)
538 NTSTATUS status;
540 if (!(call->req_packet.opcode & WREPL_OPCODE_BITS)
541 && (call->wreplconn->assoc_ctx.our_ctx == WREPLSRV_INVALID_ASSOC_CTX)) {
542 return wreplsrv_in_invalid_assoc_ctx(call);
545 switch (call->req_packet.mess_type) {
546 case WREPL_START_ASSOCIATION:
547 status = wreplsrv_in_start_association(call);
548 break;
549 case WREPL_START_ASSOCIATION_REPLY:
550 /* this is not valid here, so we ignore it */
551 return ERROR_INVALID_PARAMETER;
553 case WREPL_STOP_ASSOCIATION:
554 status = wreplsrv_in_stop_association(call);
555 break;
557 case WREPL_REPLICATION:
558 status = wreplsrv_in_replication(call);
559 break;
560 default:
561 /* everythingelse is also not valid here, so we ignore it */
562 return ERROR_INVALID_PARAMETER;
565 if (call->wreplconn->assoc_ctx.our_ctx == WREPLSRV_INVALID_ASSOC_CTX) {
566 return wreplsrv_in_invalid_assoc_ctx(call);
569 if (NT_STATUS_IS_OK(status)) {
570 /* let the backend to set some of the opcode bits, but always add the standards */
571 call->rep_packet.opcode |= WREPL_OPCODE_BITS;
572 call->rep_packet.assoc_ctx = call->wreplconn->assoc_ctx.peer_ctx;
575 return status;