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 2 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, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
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 "wrepl_server/wrepl_out_helpers.h"
30 #include "libcli/composite/composite.h"
31 #include "nbt_server/wins/winsdb.h"
32 #include "lib/ldb/include/ldb.h"
33 #include "lib/ldb/include/ldb_errors.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;
49 call->wreplconn->assoc_ctx.our_ctx = WREPLSRV_INVALID_ASSOC_CTX;
53 if (start->minor_version != 2 || start->major_version != 5) {
54 /* w2k terminate the connection if the versions doesn't match */
55 return NT_STATUS_UNKNOWN_REVISION;
58 call->wreplconn->assoc_ctx.stopped = False;
59 call->wreplconn->assoc_ctx.our_ctx = WREPLSRV_VALID_ASSOC_CTX;
60 call->wreplconn->assoc_ctx.peer_ctx = start->assoc_ctx;
62 call->rep_packet.mess_type = WREPL_START_ASSOCIATION_REPLY;
63 start_reply->assoc_ctx = call->wreplconn->assoc_ctx.our_ctx;
64 start_reply->minor_version = 2;
65 start_reply->major_version = 5;
70 static NTSTATUS wreplsrv_in_stop_assoc_ctx(struct wreplsrv_in_call *call)
72 struct wrepl_stop *stop_out = &call->rep_packet.message.stop;
74 call->wreplconn->assoc_ctx.stopped = True;
76 call->rep_packet.mess_type = WREPL_STOP_ASSOCIATION;
82 static NTSTATUS wreplsrv_in_stop_association(struct wreplsrv_in_call *call)
85 * w2k only check the assoc_ctx if the opcode has the 0x00007800 bits are set
87 if (call->req_packet.opcode & WREPL_OPCODE_BITS) {
89 *if the assoc_ctx doesn't match ignore the packet
91 if (call->req_packet.assoc_ctx != call->wreplconn->assoc_ctx.our_ctx) {
92 return ERROR_INVALID_PARAMETER;
94 /* when the opcode bits are set the connection should be directly terminated */
95 return NT_STATUS_CONNECTION_RESET;
98 if (call->wreplconn->assoc_ctx.stopped) {
99 /* this causes the connection to be directly terminated */
100 return NT_STATUS_CONNECTION_RESET;
103 /* this will cause to not receive packets anymore and terminate the connection if the reply is send */
104 call->terminate_after_send = True;
105 return wreplsrv_in_stop_assoc_ctx(call);
108 static NTSTATUS wreplsrv_in_table_query(struct wreplsrv_in_call *call)
110 struct wreplsrv_service *service = call->wreplconn->service;
111 struct wrepl_replication *repl_out = &call->rep_packet.message.replication;
112 struct wrepl_table *table_out = &call->rep_packet.message.replication.info.table;
113 const char *our_ip = call->wreplconn->our_ip;
115 repl_out->command = WREPL_REPL_TABLE_REPLY;
117 return wreplsrv_fill_wrepl_table(service, call, table_out,
118 our_ip, our_ip, True);
121 static int wreplsrv_in_sort_wins_name(struct wrepl_wins_name *n1,
122 struct wrepl_wins_name *n2)
124 if (n1->id < n2->id) return -1;
125 if (n1->id > n2->id) return 1;
129 static NTSTATUS wreplsrv_record2wins_name(TALLOC_CTX *mem_ctx,
130 const char *our_address,
131 struct wrepl_wins_name *name,
132 struct winsdb_record *rec)
135 struct wrepl_ip *ips;
137 name->name = rec->name;
138 talloc_steal(mem_ctx, rec->name);
140 name->id = rec->version;
141 name->unknown = WINSDB_GROUP_ADDRESS;
143 name->flags = WREPL_NAME_FLAGS(rec->type, rec->state, rec->node, rec->is_static);
145 switch (name->flags & 2) {
147 name->addresses.ip = rec->addresses[0]->address;
148 talloc_steal(mem_ctx, rec->addresses[0]->address);
151 num_ips = winsdb_addr_list_length(rec->addresses);
152 ips = talloc_array(mem_ctx, struct wrepl_ip, num_ips);
153 NT_STATUS_HAVE_NO_MEMORY(ips);
155 for (i = 0; i < num_ips; i++) {
156 if (strcasecmp(WINSDB_OWNER_LOCAL, rec->addresses[i]->wins_owner) == 0) {
157 ips[i].owner = talloc_strdup(ips, our_address);
158 NT_STATUS_HAVE_NO_MEMORY(ips[i].owner);
160 ips[i].owner = rec->addresses[i]->wins_owner;
161 talloc_steal(ips, rec->addresses[i]->wins_owner);
163 ips[i].ip = rec->addresses[i]->address;
164 talloc_steal(ips, rec->addresses[i]->address);
167 name->addresses.addresses.num_ips = num_ips;
168 name->addresses.addresses.ips = ips;
175 static NTSTATUS wreplsrv_in_send_request(struct wreplsrv_in_call *call)
177 struct wreplsrv_service *service = call->wreplconn->service;
178 struct wrepl_wins_owner *owner_in = &call->req_packet.message.replication.info.owner;
179 struct wrepl_replication *repl_out = &call->rep_packet.message.replication;
180 struct wrepl_send_reply *reply_out = &call->rep_packet.message.replication.info.reply;
181 struct wreplsrv_owner local_owner;
182 struct wreplsrv_owner *owner;
184 struct ldb_result *res = NULL;
186 struct wrepl_wins_name *names;
187 struct winsdb_record *rec;
191 if (strcmp(call->wreplconn->our_ip, owner_in->address) == 0) {
192 ZERO_STRUCT(local_owner);
193 local_owner.owner.address = WINSDB_OWNER_LOCAL;
194 local_owner.owner.min_version = 0;
195 local_owner.owner.max_version = wreplsrv_local_max_version(service);
196 local_owner.owner.type = 1;
197 owner = &local_owner;
199 owner = wreplsrv_find_owner(service->table, owner_in->address);
202 repl_out->command = WREPL_REPL_SEND_REPLY;
203 reply_out->num_names = 0;
204 reply_out->names = NULL;
207 * if we didn't know this owner, must be a bug in the partners client code...
208 * return an empty list.
215 * the client sends a max_version of 0, interpret it as
218 if (owner_in->max_version == 0) {
219 owner_in->max_version = (uint64_t)-1;
223 * if the partner ask for nothing, or give invalid ranges,
224 * return an empty list.
226 if (owner_in->min_version > owner_in->max_version) {
231 * if the partner has already all records for nothing, or give invalid ranges,
232 * return an empty list.
234 if (owner_in->min_version > owner->owner.max_version) {
238 filter = talloc_asprintf(call,
239 "(&(winsOwner=%s)(objectClass=winsRecord)"
240 "(|(recordState=%u)(recordState=%u))"
241 "(versionID>=%llu)(versionID<=%llu))",
242 owner->owner.address,
243 WREPL_STATE_ACTIVE, WREPL_STATE_TOMBSTONE,
244 (long long)owner_in->min_version,
245 (long long)owner_in->max_version);
246 NT_STATUS_HAVE_NO_MEMORY(filter);
247 ret = ldb_search(service->wins_db, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &res);
248 if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
249 talloc_steal(call, res);
250 if (res->count == 0) {
251 DEBUG(2,("WINSREPL:reply [%u] records owner[%s] min[%llu] max[%llu] to partner[%s]\n",
252 res->count, owner_in->address,
253 (long long)owner_in->min_version,
254 (long long)owner_in->max_version,
255 call->wreplconn->partner->address));
259 names = talloc_array(call, struct wrepl_wins_name, res->count);
260 NT_STATUS_HAVE_NO_MEMORY(names);
262 for (i = 0; i < res->count; i++) {
263 status = winsdb_record(res->msgs[i], call, &rec);
264 NT_STATUS_NOT_OK_RETURN(status);
266 status = wreplsrv_record2wins_name(names, call->wreplconn->our_ip, &names[i], rec);
267 NT_STATUS_NOT_OK_RETURN(status);
269 talloc_free(res->msgs[i]);
272 /* sort the names before we send them */
273 qsort(names, res->count, sizeof(struct wrepl_wins_name), (comparison_fn_t)wreplsrv_in_sort_wins_name);
275 DEBUG(2,("WINSREPL:reply [%u] records owner[%s] min[%llu] max[%llu] to partner[%s]\n",
276 res->count, owner_in->address,
277 (long long)owner_in->min_version,
278 (long long)owner_in->max_version,
279 call->wreplconn->partner->address));
281 reply_out->num_names = res->count;
282 reply_out->names = names;
287 struct wreplsrv_in_update_state {
288 struct wreplsrv_in_connection *wrepl_in;
289 struct wreplsrv_out_connection *wrepl_out;
290 struct composite_context *creq;
291 struct wreplsrv_pull_cycle_io cycle_io;
294 static void wreplsrv_in_update_handler(struct composite_context *creq)
296 struct wreplsrv_in_update_state *update_state = talloc_get_type(creq->async.private_data,
297 struct wreplsrv_in_update_state);
300 status = wreplsrv_pull_cycle_recv(creq);
302 talloc_free(update_state->wrepl_out);
304 wreplsrv_terminate_in_connection(update_state->wrepl_in, nt_errstr(status));
307 static NTSTATUS wreplsrv_in_update(struct wreplsrv_in_call *call)
309 struct wreplsrv_in_connection *wrepl_in = call->wreplconn;
310 struct wreplsrv_out_connection *wrepl_out;
311 struct wrepl_table *update_in = &call->req_packet.message.replication.info.table;
312 struct wreplsrv_in_update_state *update_state;
315 DEBUG(2,("WREPL_REPL_UPDATE: partner[%s] initiator[%s] num_owners[%u]\n",
316 call->wreplconn->partner->address,
317 update_in->initiator, update_in->partner_count));
320 * we need to flip the connection into a client connection
321 * and do a WREPL_REPL_SEND_REQUEST's on the that connection
322 * and then stop this connection
324 fde_flags = event_get_fd_flags(wrepl_in->conn->event.fde);
325 talloc_free(wrepl_in->conn->event.fde);
326 wrepl_in->conn->event.fde = NULL;
328 update_state = talloc(wrepl_in, struct wreplsrv_in_update_state);
329 NT_STATUS_HAVE_NO_MEMORY(update_state);
331 wrepl_out = talloc(update_state, struct wreplsrv_out_connection);
332 NT_STATUS_HAVE_NO_MEMORY(wrepl_out);
333 wrepl_out->service = wrepl_in->service;
334 wrepl_out->partner = wrepl_in->partner;
335 wrepl_out->assoc_ctx.our_ctx = wrepl_in->assoc_ctx.our_ctx;
336 wrepl_out->assoc_ctx.peer_ctx = wrepl_in->assoc_ctx.peer_ctx;
337 wrepl_out->sock = wrepl_socket_merge(wrepl_out,
338 wrepl_in->conn->event.ctx,
339 wrepl_in->conn->socket,
341 NT_STATUS_HAVE_NO_MEMORY(wrepl_out->sock);
343 event_set_fd_flags(wrepl_out->sock->event.fde, fde_flags);
345 update_state->wrepl_in = wrepl_in;
346 update_state->wrepl_out = wrepl_out;
347 update_state->cycle_io.in.partner = wrepl_out->partner;
348 update_state->cycle_io.in.num_owners = update_in->partner_count;
349 update_state->cycle_io.in.owners = update_in->partners;
350 talloc_steal(update_state, update_in->partners);
351 update_state->cycle_io.in.wreplconn = wrepl_out;
352 update_state->creq = wreplsrv_pull_cycle_send(update_state, &update_state->cycle_io);
353 if (!update_state->creq) {
354 return NT_STATUS_INTERNAL_ERROR;
357 update_state->creq->async.fn = wreplsrv_in_update_handler;
358 update_state->creq->async.private_data = update_state;
360 return ERROR_INVALID_PARAMETER;
363 static NTSTATUS wreplsrv_in_update2(struct wreplsrv_in_call *call)
365 return wreplsrv_in_update(call);
368 static NTSTATUS wreplsrv_in_inform(struct wreplsrv_in_call *call)
370 struct wrepl_table *inform_in = &call->req_packet.message.replication.info.table;
372 DEBUG(2,("WREPL_REPL_INFORM: partner[%s] initiator[%s] num_owners[%u]\n",
373 call->wreplconn->partner->address,
374 inform_in->initiator, inform_in->partner_count));
376 wreplsrv_out_partner_pull(call->wreplconn->partner, inform_in);
378 /* we don't reply to WREPL_REPL_INFORM messages */
379 return ERROR_INVALID_PARAMETER;
382 static NTSTATUS wreplsrv_in_inform2(struct wreplsrv_in_call *call)
384 return wreplsrv_in_inform(call);
387 static NTSTATUS wreplsrv_in_replication(struct wreplsrv_in_call *call)
389 struct wrepl_replication *repl_in = &call->req_packet.message.replication;
393 * w2k only check the assoc_ctx if the opcode has the 0x00007800 bits are set
395 if (call->req_packet.opcode & WREPL_OPCODE_BITS) {
397 *if the assoc_ctx doesn't match ignore the packet
399 if (call->req_packet.assoc_ctx != call->wreplconn->assoc_ctx.our_ctx) {
400 return ERROR_INVALID_PARAMETER;
404 if (!call->wreplconn->partner) {
405 DEBUG(1,("Failing WINS replication from non-partner %s\n",
406 socket_get_peer_addr(call->wreplconn->conn->socket, call)));
407 return wreplsrv_in_stop_assoc_ctx(call);
410 switch (repl_in->command) {
411 case WREPL_REPL_TABLE_QUERY:
412 if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PUSH)) {
413 return wreplsrv_in_stop_assoc_ctx(call);
415 status = wreplsrv_in_table_query(call);
418 case WREPL_REPL_TABLE_REPLY:
419 return ERROR_INVALID_PARAMETER;
421 case WREPL_REPL_SEND_REQUEST:
422 if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PUSH)) {
423 return wreplsrv_in_stop_assoc_ctx(call);
425 status = wreplsrv_in_send_request(call);
428 case WREPL_REPL_SEND_REPLY:
429 return ERROR_INVALID_PARAMETER;
431 case WREPL_REPL_UPDATE:
432 if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
433 return wreplsrv_in_stop_assoc_ctx(call);
435 status = wreplsrv_in_update(call);
438 case WREPL_REPL_UPDATE2:
439 if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
440 return wreplsrv_in_stop_assoc_ctx(call);
442 status = wreplsrv_in_update2(call);
445 case WREPL_REPL_INFORM:
446 if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
447 return wreplsrv_in_stop_assoc_ctx(call);
449 status = wreplsrv_in_inform(call);
452 case WREPL_REPL_INFORM2:
453 if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
454 return wreplsrv_in_stop_assoc_ctx(call);
456 status = wreplsrv_in_inform2(call);
460 return ERROR_INVALID_PARAMETER;
463 if (NT_STATUS_IS_OK(status)) {
464 call->rep_packet.mess_type = WREPL_REPLICATION;
470 static NTSTATUS wreplsrv_in_invalid_assoc_ctx(struct wreplsrv_in_call *call)
472 struct wrepl_start *start = &call->rep_packet.message.start;
474 call->rep_packet.opcode = 0x00008583;
475 call->rep_packet.assoc_ctx = 0;
476 call->rep_packet.mess_type = WREPL_START_ASSOCIATION;
478 start->assoc_ctx = 0x0000000a;
479 start->minor_version = 0x0001;
480 start->major_version = 0x0000;
482 call->rep_packet.padding = data_blob_talloc(call, NULL, 4);
483 memset(call->rep_packet.padding.data, '\0', call->rep_packet.padding.length);
488 NTSTATUS wreplsrv_in_call(struct wreplsrv_in_call *call)
492 if (!(call->req_packet.opcode & WREPL_OPCODE_BITS)
493 && (call->wreplconn->assoc_ctx.our_ctx == WREPLSRV_INVALID_ASSOC_CTX)) {
494 return wreplsrv_in_invalid_assoc_ctx(call);
497 switch (call->req_packet.mess_type) {
498 case WREPL_START_ASSOCIATION:
499 status = wreplsrv_in_start_association(call);
501 case WREPL_START_ASSOCIATION_REPLY:
502 /* this is not valid here, so we ignore it */
503 return ERROR_INVALID_PARAMETER;
505 case WREPL_STOP_ASSOCIATION:
506 status = wreplsrv_in_stop_association(call);
509 case WREPL_REPLICATION:
510 status = wreplsrv_in_replication(call);
513 /* everythingelse is also not valid here, so we ignore it */
514 return ERROR_INVALID_PARAMETER;
517 if (call->wreplconn->assoc_ctx.our_ctx == WREPLSRV_INVALID_ASSOC_CTX) {
518 return wreplsrv_in_invalid_assoc_ctx(call);
521 if (NT_STATUS_IS_OK(status)) {
522 /* let the backend to set some of the opcode bits, but always add the standards */
523 call->rep_packet.opcode |= WREPL_OPCODE_BITS;
524 call->rep_packet.assoc_ctx = call->wreplconn->assoc_ctx.peer_ctx;