gensec: recv_handler can't be NULL at that point.
[Samba.git] / source4 / wrepl_server / wrepl_in_call.c
blobe34922e270c7ba14297aeab341c7ae58a75df796
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/tsocket/tsocket.h"
25 #include "smbd/service_task.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 <ldb.h>
32 #include <ldb_errors.h>
33 #include "system/time.h"
34 #include "lib/util/tsort.h"
35 #include "param/param.h"
37 static NTSTATUS wreplsrv_in_start_association(struct wreplsrv_in_call *call)
39 struct wrepl_start *start = &call->req_packet.message.start;
40 struct wrepl_start *start_reply = &call->rep_packet.message.start_reply;
42 if (call->req_packet.opcode & WREPL_OPCODE_BITS) {
44 *if the assoc_ctx doesn't match ignore the packet
46 if ((call->req_packet.assoc_ctx != call->wreplconn->assoc_ctx.our_ctx)
47 && (call->req_packet.assoc_ctx != 0)) {
48 return ERROR_INVALID_PARAMETER;
50 } else {
51 call->wreplconn->assoc_ctx.our_ctx = WREPLSRV_INVALID_ASSOC_CTX;
52 return NT_STATUS_OK;
56 * it seems that we don't know all details about the start_association
57 * to support replication with NT4 (it sends 1.1 instead of 5.2)
58 * we ignore the version numbers until we know all details
60 #if 0
61 if (start->minor_version != 2 || start->major_version != 5) {
62 /* w2k terminate the connection if the versions doesn't match */
63 return NT_STATUS_UNKNOWN_REVISION;
65 #endif
67 call->wreplconn->assoc_ctx.stopped = false;
68 call->wreplconn->assoc_ctx.our_ctx = WREPLSRV_VALID_ASSOC_CTX;
69 call->wreplconn->assoc_ctx.peer_ctx = start->assoc_ctx;
71 call->rep_packet.mess_type = WREPL_START_ASSOCIATION_REPLY;
72 start_reply->assoc_ctx = call->wreplconn->assoc_ctx.our_ctx;
73 start_reply->minor_version = 2;
74 start_reply->major_version = 5;
77 * nt4 uses 41 bytes for the start_association call
78 * so do it the same and as we don't know the meanings of this bytes
79 * we just send zeros and nt4, w2k and w2k3 seems to be happy with this
81 * if we don't do this nt4 uses an old version of the wins replication protocol
82 * and that would break nt4 <-> samba replication
84 call->rep_packet.padding = data_blob_talloc(call, NULL, 21);
85 NT_STATUS_HAVE_NO_MEMORY(call->rep_packet.padding.data);
87 memset(call->rep_packet.padding.data, 0, call->rep_packet.padding.length);
89 return NT_STATUS_OK;
92 static NTSTATUS wreplsrv_in_stop_assoc_ctx(struct wreplsrv_in_call *call)
94 struct wrepl_stop *stop_out = &call->rep_packet.message.stop;
96 call->wreplconn->assoc_ctx.stopped = true;
98 call->rep_packet.mess_type = WREPL_STOP_ASSOCIATION;
99 stop_out->reason = 4;
101 return NT_STATUS_OK;
104 static NTSTATUS wreplsrv_in_stop_association(struct wreplsrv_in_call *call)
107 * w2k only check the assoc_ctx if the opcode has the 0x00007800 bits are set
109 if (call->req_packet.opcode & WREPL_OPCODE_BITS) {
111 *if the assoc_ctx doesn't match ignore the packet
113 if (call->req_packet.assoc_ctx != call->wreplconn->assoc_ctx.our_ctx) {
114 return ERROR_INVALID_PARAMETER;
116 /* when the opcode bits are set the connection should be directly terminated */
117 return NT_STATUS_CONNECTION_RESET;
120 if (call->wreplconn->assoc_ctx.stopped) {
121 /* this causes the connection to be directly terminated */
122 return NT_STATUS_CONNECTION_RESET;
125 /* this will cause to not receive packets anymore and terminate the connection if the reply is send */
126 call->terminate_after_send = true;
127 return wreplsrv_in_stop_assoc_ctx(call);
130 static NTSTATUS wreplsrv_in_table_query(struct wreplsrv_in_call *call)
132 struct wreplsrv_service *service = call->wreplconn->service;
133 struct wrepl_replication *repl_out = &call->rep_packet.message.replication;
134 struct wrepl_table *table_out = &call->rep_packet.message.replication.info.table;
136 repl_out->command = WREPL_REPL_TABLE_REPLY;
138 return wreplsrv_fill_wrepl_table(service, call, table_out,
139 service->wins_db->local_owner, true);
142 static int wreplsrv_in_sort_wins_name(struct wrepl_wins_name *n1,
143 struct wrepl_wins_name *n2)
145 if (n1->id < n2->id) return -1;
146 if (n1->id > n2->id) return 1;
147 return 0;
150 static NTSTATUS wreplsrv_record2wins_name(TALLOC_CTX *mem_ctx,
151 struct wrepl_wins_name *name,
152 struct winsdb_record *rec)
154 uint32_t num_ips, i;
155 struct wrepl_ip *ips;
157 name->name = rec->name;
158 talloc_steal(mem_ctx, rec->name);
160 name->id = rec->version;
161 name->unknown = "255.255.255.255";
163 name->flags = WREPL_NAME_FLAGS(rec->type, rec->state, rec->node, rec->is_static);
165 switch (name->flags & 2) {
166 case 0:
167 name->addresses.ip = rec->addresses[0]->address;
168 talloc_steal(mem_ctx, rec->addresses[0]->address);
169 break;
170 case 2:
171 num_ips = winsdb_addr_list_length(rec->addresses);
172 ips = talloc_array(mem_ctx, struct wrepl_ip, num_ips);
173 NT_STATUS_HAVE_NO_MEMORY(ips);
175 for (i = 0; i < num_ips; i++) {
176 ips[i].owner = rec->addresses[i]->wins_owner;
177 talloc_steal(ips, rec->addresses[i]->wins_owner);
178 ips[i].ip = rec->addresses[i]->address;
179 talloc_steal(ips, rec->addresses[i]->address);
182 name->addresses.addresses.num_ips = num_ips;
183 name->addresses.addresses.ips = ips;
184 break;
187 return NT_STATUS_OK;
190 static NTSTATUS wreplsrv_in_send_request(struct wreplsrv_in_call *call)
192 struct wreplsrv_service *service = call->wreplconn->service;
193 struct wrepl_wins_owner *owner_in = &call->req_packet.message.replication.info.owner;
194 struct wrepl_replication *repl_out = &call->rep_packet.message.replication;
195 struct wrepl_send_reply *reply_out = &call->rep_packet.message.replication.info.reply;
196 struct wreplsrv_owner *owner;
197 const char *owner_filter;
198 const char *filter;
199 struct ldb_result *res = NULL;
200 int ret;
201 struct wrepl_wins_name *names;
202 struct winsdb_record *rec;
203 NTSTATUS status;
204 unsigned int i, j;
205 time_t now = time(NULL);
207 owner = wreplsrv_find_owner(service, service->table, owner_in->address);
209 repl_out->command = WREPL_REPL_SEND_REPLY;
210 reply_out->num_names = 0;
211 reply_out->names = NULL;
214 * if we didn't know this owner, must be a bug in the partners client code...
215 * return an empty list.
217 if (!owner) {
218 DEBUG(2,("WINSREPL:reply [0] records unknown owner[%s] to partner[%s]\n",
219 owner_in->address, call->wreplconn->partner->address));
220 return NT_STATUS_OK;
224 * the client sends a max_version of 0, interpret it as
225 * (uint64_t)-1
227 if (owner_in->max_version == 0) {
228 owner_in->max_version = (uint64_t)-1;
232 * if the partner ask for nothing, or give invalid ranges,
233 * return an empty list.
235 if (owner_in->min_version > owner_in->max_version) {
236 DEBUG(2,("WINSREPL:reply [0] records owner[%s] min[%llu] max[%llu] to partner[%s]\n",
237 owner_in->address,
238 (long long)owner_in->min_version,
239 (long long)owner_in->max_version,
240 call->wreplconn->partner->address));
241 return NT_STATUS_OK;
245 * if the partner has already all records for nothing, or give invalid ranges,
246 * return an empty list.
248 if (owner_in->min_version > owner->owner.max_version) {
249 DEBUG(2,("WINSREPL:reply [0] records owner[%s] min[%llu] max[%llu] to partner[%s]\n",
250 owner_in->address,
251 (long long)owner_in->min_version,
252 (long long)owner_in->max_version,
253 call->wreplconn->partner->address));
254 return NT_STATUS_OK;
257 owner_filter = wreplsrv_owner_filter(service, call, owner->owner.address);
258 NT_STATUS_HAVE_NO_MEMORY(owner_filter);
259 filter = talloc_asprintf(call,
260 "(&%s(objectClass=winsRecord)"
261 "(|(recordState=%u)(recordState=%u))"
262 "(versionID>=%llu)(versionID<=%llu))",
263 owner_filter,
264 WREPL_STATE_ACTIVE, WREPL_STATE_TOMBSTONE,
265 (long long)owner_in->min_version,
266 (long long)owner_in->max_version);
267 NT_STATUS_HAVE_NO_MEMORY(filter);
268 ret = ldb_search(service->wins_db->ldb, call, &res, NULL, LDB_SCOPE_SUBTREE, NULL, "%s", filter);
269 if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
270 DEBUG(10,("WINSREPL: filter '%s' count %d\n", filter, res->count));
272 if (res->count == 0) {
273 DEBUG(2,("WINSREPL:reply [%u] records owner[%s] min[%llu] max[%llu] to partner[%s]\n",
274 res->count, owner_in->address,
275 (long long)owner_in->min_version,
276 (long long)owner_in->max_version,
277 call->wreplconn->partner->address));
278 return NT_STATUS_OK;
281 names = talloc_array(call, struct wrepl_wins_name, res->count);
282 NT_STATUS_HAVE_NO_MEMORY(names);
284 for (i=0, j=0; i < res->count; i++) {
285 status = winsdb_record(service->wins_db, res->msgs[i], call, now, &rec);
286 NT_STATUS_NOT_OK_RETURN(status);
289 * it's possible that winsdb_record() made the record RELEASED
290 * because it's expired, but in the database it's still stored
291 * as ACTIVE...
293 * make sure we really only replicate ACTIVE and TOMBSTONE records
295 if (rec->state == WREPL_STATE_ACTIVE || rec->state == WREPL_STATE_TOMBSTONE) {
296 status = wreplsrv_record2wins_name(names, &names[j], rec);
297 NT_STATUS_NOT_OK_RETURN(status);
298 j++;
301 talloc_free(rec);
302 talloc_free(res->msgs[i]);
305 /* sort the names before we send them */
306 TYPESAFE_QSORT(names, j, wreplsrv_in_sort_wins_name);
308 DEBUG(2,("WINSREPL:reply [%u] records owner[%s] min[%llu] max[%llu] to partner[%s]\n",
309 j, owner_in->address,
310 (long long)owner_in->min_version,
311 (long long)owner_in->max_version,
312 call->wreplconn->partner->address));
314 reply_out->num_names = j;
315 reply_out->names = names;
317 return NT_STATUS_OK;
320 struct wreplsrv_in_update_state {
321 struct wreplsrv_in_connection *wrepl_in;
322 struct wreplsrv_out_connection *wrepl_out;
323 struct composite_context *creq;
324 struct wreplsrv_pull_cycle_io cycle_io;
327 static void wreplsrv_in_update_handler(struct composite_context *creq)
329 struct wreplsrv_in_update_state *update_state = talloc_get_type(creq->async.private_data,
330 struct wreplsrv_in_update_state);
331 NTSTATUS status;
333 status = wreplsrv_pull_cycle_recv(creq);
335 talloc_free(update_state->wrepl_out);
337 wreplsrv_terminate_in_connection(update_state->wrepl_in, nt_errstr(status));
340 static NTSTATUS wreplsrv_in_update(struct wreplsrv_in_call *call)
342 struct wreplsrv_in_connection *wrepl_in = call->wreplconn;
343 struct wreplsrv_out_connection *wrepl_out;
344 struct wrepl_table *update_in = &call->req_packet.message.replication.info.table;
345 struct wreplsrv_in_update_state *update_state;
346 NTSTATUS status;
348 DEBUG(2,("WREPL_REPL_UPDATE: partner[%s] initiator[%s] num_owners[%u]\n",
349 call->wreplconn->partner->address,
350 update_in->initiator, update_in->partner_count));
352 update_state = talloc(wrepl_in, struct wreplsrv_in_update_state);
353 NT_STATUS_HAVE_NO_MEMORY(update_state);
355 wrepl_out = talloc(update_state, struct wreplsrv_out_connection);
356 NT_STATUS_HAVE_NO_MEMORY(wrepl_out);
357 wrepl_out->service = wrepl_in->service;
358 wrepl_out->partner = wrepl_in->partner;
359 wrepl_out->assoc_ctx.our_ctx = wrepl_in->assoc_ctx.our_ctx;
360 wrepl_out->assoc_ctx.peer_ctx = wrepl_in->assoc_ctx.peer_ctx;
361 wrepl_out->sock = wrepl_socket_init(wrepl_out,
362 wrepl_in->conn->event.ctx);
364 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(wrepl_out->sock, update_state);
366 TALLOC_FREE(wrepl_in->send_queue);
368 status = wrepl_socket_donate_stream(wrepl_out->sock, &wrepl_in->tstream);
369 NT_STATUS_NOT_OK_RETURN_AND_FREE(status, update_state);
371 update_state->wrepl_in = wrepl_in;
372 update_state->wrepl_out = wrepl_out;
373 update_state->cycle_io.in.partner = wrepl_out->partner;
374 update_state->cycle_io.in.num_owners = update_in->partner_count;
375 update_state->cycle_io.in.owners = update_in->partners;
376 talloc_steal(update_state, update_in->partners);
377 update_state->cycle_io.in.wreplconn = wrepl_out;
378 update_state->creq = wreplsrv_pull_cycle_send(update_state, &update_state->cycle_io);
379 if (!update_state->creq) {
380 talloc_free(update_state);
381 return NT_STATUS_INTERNAL_ERROR;
384 update_state->creq->async.fn = wreplsrv_in_update_handler;
385 update_state->creq->async.private_data = update_state;
387 return ERROR_INVALID_PARAMETER;
390 static NTSTATUS wreplsrv_in_update2(struct wreplsrv_in_call *call)
392 return wreplsrv_in_update(call);
395 static NTSTATUS wreplsrv_in_inform(struct wreplsrv_in_call *call)
397 struct wrepl_table *inform_in = &call->req_packet.message.replication.info.table;
399 DEBUG(2,("WREPL_REPL_INFORM: partner[%s] initiator[%s] num_owners[%u]\n",
400 call->wreplconn->partner->address,
401 inform_in->initiator, inform_in->partner_count));
403 wreplsrv_out_partner_pull(call->wreplconn->partner, inform_in);
405 /* we don't reply to WREPL_REPL_INFORM messages */
406 return ERROR_INVALID_PARAMETER;
409 static NTSTATUS wreplsrv_in_inform2(struct wreplsrv_in_call *call)
411 return wreplsrv_in_inform(call);
414 static NTSTATUS wreplsrv_in_replication(struct wreplsrv_in_call *call)
416 struct wrepl_replication *repl_in = &call->req_packet.message.replication;
417 NTSTATUS status;
420 * w2k only check the assoc_ctx if the opcode has the 0x00007800 bits are set
422 if (call->req_packet.opcode & WREPL_OPCODE_BITS) {
424 *if the assoc_ctx doesn't match ignore the packet
426 if (call->req_packet.assoc_ctx != call->wreplconn->assoc_ctx.our_ctx) {
427 return ERROR_INVALID_PARAMETER;
431 if (!call->wreplconn->partner) {
432 struct tsocket_address *peer_addr = call->wreplconn->conn->remote_address;
433 char *peer_ip;
435 if (!tsocket_address_is_inet(peer_addr, "ipv4")) {
436 DEBUG(0,("wreplsrv_in_replication: non ipv4 peer addr '%s'\n",
437 tsocket_address_string(peer_addr, call)));
438 return NT_STATUS_INTERNAL_ERROR;
441 peer_ip = tsocket_address_inet_addr_string(peer_addr, call);
442 if (peer_ip == NULL) {
443 return NT_STATUS_NO_MEMORY;
446 call->wreplconn->partner = wreplsrv_find_partner(call->wreplconn->service, peer_ip);
447 if (!call->wreplconn->partner) {
448 DEBUG(1,("Failing WINS replication from non-partner %s\n", peer_ip));
449 return wreplsrv_in_stop_assoc_ctx(call);
453 switch (repl_in->command) {
454 case WREPL_REPL_TABLE_QUERY:
455 if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PUSH)) {
456 DEBUG(0,("Failing WINS replication TABLE_QUERY from non-push-partner %s\n",
457 call->wreplconn->partner->address));
458 return wreplsrv_in_stop_assoc_ctx(call);
460 status = wreplsrv_in_table_query(call);
461 break;
463 case WREPL_REPL_TABLE_REPLY:
464 return ERROR_INVALID_PARAMETER;
466 case WREPL_REPL_SEND_REQUEST:
467 if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PUSH)) {
468 DEBUG(0,("Failing WINS replication SEND_REQUESET from non-push-partner %s\n",
469 call->wreplconn->partner->address));
470 return wreplsrv_in_stop_assoc_ctx(call);
472 status = wreplsrv_in_send_request(call);
473 break;
475 case WREPL_REPL_SEND_REPLY:
476 return ERROR_INVALID_PARAMETER;
478 case WREPL_REPL_UPDATE:
479 if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
480 DEBUG(0,("Failing WINS replication UPDATE from non-pull-partner %s\n",
481 call->wreplconn->partner->address));
482 return wreplsrv_in_stop_assoc_ctx(call);
484 status = wreplsrv_in_update(call);
485 break;
487 case WREPL_REPL_UPDATE2:
488 if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
489 DEBUG(0,("Failing WINS replication UPDATE2 from non-pull-partner %s\n",
490 call->wreplconn->partner->address));
491 return wreplsrv_in_stop_assoc_ctx(call);
493 status = wreplsrv_in_update2(call);
494 break;
496 case WREPL_REPL_INFORM:
497 if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
498 DEBUG(0,("Failing WINS replication INFORM from non-pull-partner %s\n",
499 call->wreplconn->partner->address));
500 return wreplsrv_in_stop_assoc_ctx(call);
502 status = wreplsrv_in_inform(call);
503 break;
505 case WREPL_REPL_INFORM2:
506 if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
507 DEBUG(0,("Failing WINS replication INFORM2 from non-pull-partner %s\n",
508 call->wreplconn->partner->address));
509 return wreplsrv_in_stop_assoc_ctx(call);
511 status = wreplsrv_in_inform2(call);
512 break;
514 default:
515 return ERROR_INVALID_PARAMETER;
518 if (NT_STATUS_IS_OK(status)) {
519 call->rep_packet.mess_type = WREPL_REPLICATION;
522 return status;
525 static NTSTATUS wreplsrv_in_invalid_assoc_ctx(struct wreplsrv_in_call *call)
527 struct wrepl_start *start = &call->rep_packet.message.start;
529 call->rep_packet.opcode = 0x00008583;
530 call->rep_packet.assoc_ctx = 0;
531 call->rep_packet.mess_type = WREPL_START_ASSOCIATION;
533 start->assoc_ctx = 0x0000000a;
534 start->minor_version = 0x0001;
535 start->major_version = 0x0000;
537 call->rep_packet.padding = data_blob_talloc(call, NULL, 4);
538 memset(call->rep_packet.padding.data, '\0', call->rep_packet.padding.length);
540 return NT_STATUS_OK;
543 NTSTATUS wreplsrv_in_call(struct wreplsrv_in_call *call)
545 NTSTATUS status;
547 if (!(call->req_packet.opcode & WREPL_OPCODE_BITS)
548 && (call->wreplconn->assoc_ctx.our_ctx == WREPLSRV_INVALID_ASSOC_CTX)) {
549 return wreplsrv_in_invalid_assoc_ctx(call);
552 switch (call->req_packet.mess_type) {
553 case WREPL_START_ASSOCIATION:
554 status = wreplsrv_in_start_association(call);
555 break;
556 case WREPL_START_ASSOCIATION_REPLY:
557 /* this is not valid here, so we ignore it */
558 return ERROR_INVALID_PARAMETER;
560 case WREPL_STOP_ASSOCIATION:
561 status = wreplsrv_in_stop_association(call);
562 break;
564 case WREPL_REPLICATION:
565 status = wreplsrv_in_replication(call);
566 break;
567 default:
568 /* everythingelse is also not valid here, so we ignore it */
569 return ERROR_INVALID_PARAMETER;
572 if (call->wreplconn->assoc_ctx.our_ctx == WREPLSRV_INVALID_ASSOC_CTX) {
573 return wreplsrv_in_invalid_assoc_ctx(call);
576 if (NT_STATUS_IS_OK(status)) {
577 /* let the backend to set some of the opcode bits, but always add the standards */
578 call->rep_packet.opcode |= WREPL_OPCODE_BITS;
579 call->rep_packet.assoc_ctx = call->wreplconn->assoc_ctx.peer_ctx;
582 return status;