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 
wreplsrv_in_start_association(struct wreplsrv_in_call * call)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 
wreplsrv_in_stop_assoc_ctx(struct wreplsrv_in_call * call)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 
wreplsrv_in_stop_association(struct wreplsrv_in_call * call)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 
wreplsrv_in_table_query(struct wreplsrv_in_call * call)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 
wreplsrv_in_sort_wins_name(struct wrepl_wins_name * n1,struct wrepl_wins_name * n2)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 
wreplsrv_record2wins_name(TALLOC_CTX * mem_ctx,struct wrepl_wins_name * name,struct winsdb_record * rec)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 
wreplsrv_in_send_request(struct wreplsrv_in_call * call)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 
wreplsrv_in_update_handler(struct composite_context * creq)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 
wreplsrv_in_update(struct wreplsrv_in_call * call)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 
wreplsrv_in_update2(struct wreplsrv_in_call * call)395 static NTSTATUS wreplsrv_in_update2(struct wreplsrv_in_call *call)
396 {
397 	return wreplsrv_in_update(call);
398 }
399 
wreplsrv_in_inform(struct wreplsrv_in_call * call)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 
wreplsrv_in_inform2(struct wreplsrv_in_call * call)414 static NTSTATUS wreplsrv_in_inform2(struct wreplsrv_in_call *call)
415 {
416 	return wreplsrv_in_inform(call);
417 }
418 
wreplsrv_in_replication(struct wreplsrv_in_call * call)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 
wreplsrv_in_invalid_assoc_ctx(struct wreplsrv_in_call * call)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 
wreplsrv_in_call(struct wreplsrv_in_call * call)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 }
578