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