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/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"
36 
wreplsrv_in_start_association(struct wreplsrv_in_call * call)37 static NTSTATUS wreplsrv_in_start_association(struct wreplsrv_in_call *call)
38 {
39 	struct wrepl_start *start	= &call->req_packet.message.start;
40 	struct wrepl_start *start_reply	= &call->rep_packet.message.start_reply;
41 
42 	if (call->req_packet.opcode & WREPL_OPCODE_BITS) {
43 		/*
44 		 *if the assoc_ctx doesn't match ignore the packet
45 		 */
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;
49 		}
50 	} else {
51 		call->wreplconn->assoc_ctx.our_ctx = WREPLSRV_INVALID_ASSOC_CTX;
52 		return NT_STATUS_OK;
53 	}
54 
55 /*
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
59  */
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;
64 	}
65 #endif
66 
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;
70 
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;
75 
76 	/*
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
80 	 *
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
83 	 */
84 	call->rep_packet.padding		= data_blob_talloc(call, NULL, 21);
85 	NT_STATUS_HAVE_NO_MEMORY(call->rep_packet.padding.data);
86 
87 	memset(call->rep_packet.padding.data, 0, call->rep_packet.padding.length);
88 
89 	return NT_STATUS_OK;
90 }
91 
wreplsrv_in_stop_assoc_ctx(struct wreplsrv_in_call * call)92 static NTSTATUS wreplsrv_in_stop_assoc_ctx(struct wreplsrv_in_call *call)
93 {
94 	struct wrepl_stop *stop_out		= &call->rep_packet.message.stop;
95 
96 	call->wreplconn->assoc_ctx.stopped	= true;
97 
98 	call->rep_packet.mess_type		= WREPL_STOP_ASSOCIATION;
99 	stop_out->reason			= 4;
100 
101 	return NT_STATUS_OK;
102 }
103 
wreplsrv_in_stop_association(struct wreplsrv_in_call * call)104 static NTSTATUS wreplsrv_in_stop_association(struct wreplsrv_in_call *call)
105 {
106 	/*
107 	 * w2k only check the assoc_ctx if the opcode has the 0x00007800 bits are set
108 	 */
109 	if (call->req_packet.opcode & WREPL_OPCODE_BITS) {
110 		/*
111 		 *if the assoc_ctx doesn't match ignore the packet
112 		 */
113 		if (call->req_packet.assoc_ctx != call->wreplconn->assoc_ctx.our_ctx) {
114 			return ERROR_INVALID_PARAMETER;
115 		}
116 		/* when the opcode bits are set the connection should be directly terminated */
117 		return NT_STATUS_CONNECTION_RESET;
118 	}
119 
120 	if (call->wreplconn->assoc_ctx.stopped) {
121 		/* this causes the connection to be directly terminated */
122 		return NT_STATUS_CONNECTION_RESET;
123 	}
124 
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);
128 }
129 
wreplsrv_in_table_query(struct wreplsrv_in_call * call)130 static NTSTATUS wreplsrv_in_table_query(struct wreplsrv_in_call *call)
131 {
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;
135 
136 	repl_out->command = WREPL_REPL_TABLE_REPLY;
137 
138 	return wreplsrv_fill_wrepl_table(service, call, table_out,
139 					 service->wins_db->local_owner, true);
140 }
141 
wreplsrv_in_sort_wins_name(struct wrepl_wins_name * n1,struct wrepl_wins_name * n2)142 static int wreplsrv_in_sort_wins_name(struct wrepl_wins_name *n1,
143 				      struct wrepl_wins_name *n2)
144 {
145 	if (n1->id < n2->id) return -1;
146 	if (n1->id > n2->id) return 1;
147 	return 0;
148 }
149 
wreplsrv_record2wins_name(TALLOC_CTX * mem_ctx,struct wrepl_wins_name * name,struct winsdb_record * rec)150 static NTSTATUS wreplsrv_record2wins_name(TALLOC_CTX *mem_ctx,
151 					  struct wrepl_wins_name *name,
152 					  struct winsdb_record *rec)
153 {
154 	uint32_t num_ips, i;
155 	struct wrepl_ip *ips;
156 
157 	name->name		= rec->name;
158 	talloc_steal(mem_ctx, rec->name);
159 
160 	name->id		= rec->version;
161 	name->unknown		= "255.255.255.255";
162 
163 	name->flags		= WREPL_NAME_FLAGS(rec->type, rec->state, rec->node, rec->is_static);
164 
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);
174 
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);
180 		}
181 
182 		name->addresses.addresses.num_ips	= num_ips;
183 		name->addresses.addresses.ips		= ips;
184 		break;
185 	}
186 
187 	return NT_STATUS_OK;
188 }
189 
wreplsrv_in_send_request(struct wreplsrv_in_call * call)190 static NTSTATUS wreplsrv_in_send_request(struct wreplsrv_in_call *call)
191 {
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);
206 
207 	owner = wreplsrv_find_owner(service, service->table, owner_in->address);
208 
209 	repl_out->command	= WREPL_REPL_SEND_REPLY;
210 	reply_out->num_names	= 0;
211 	reply_out->names	= NULL;
212 
213 	/*
214 	 * if we didn't know this owner, must be a bug in the partners client code...
215 	 * return an empty list.
216 	 */
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;
221 	}
222 
223 	/*
224 	 * the client sends a max_version of 0, interpret it as
225 	 * (uint64_t)-1
226 	 */
227 	if (owner_in->max_version == 0) {
228 		owner_in->max_version = (uint64_t)-1;
229 	}
230 
231 	/*
232 	 * if the partner ask for nothing, or give invalid ranges,
233 	 * return an empty list.
234 	 */
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;
242 	}
243 
244 	/*
245 	 * if the partner has already all records for nothing, or give invalid ranges,
246 	 * return an empty list.
247 	 */
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;
255 	}
256 
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));
271 
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;
279 	}
280 
281 	names = talloc_array(call, struct wrepl_wins_name, res->count);
282 	NT_STATUS_HAVE_NO_MEMORY(names);
283 
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);
287 
288 		/*
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...
292 		 *
293 		 * make sure we really only replicate ACTIVE and TOMBSTONE records
294 		 */
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++;
299 		}
300 
301 		talloc_free(rec);
302 		talloc_free(res->msgs[i]);
303 	}
304 
305 	/* sort the names before we send them */
306 	TYPESAFE_QSORT(names, j, wreplsrv_in_sort_wins_name);
307 
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));
313 
314 	reply_out->num_names	= j;
315 	reply_out->names	= names;
316 
317 	return NT_STATUS_OK;
318 }
319 
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;
325 };
326 
wreplsrv_in_update_handler(struct composite_context * creq)327 static void wreplsrv_in_update_handler(struct composite_context *creq)
328 {
329 	struct wreplsrv_in_update_state *update_state = talloc_get_type(creq->async.private_data,
330 							struct wreplsrv_in_update_state);
331 	NTSTATUS status;
332 
333 	status = wreplsrv_pull_cycle_recv(creq);
334 
335 	talloc_free(update_state->wrepl_out);
336 
337 	wreplsrv_terminate_in_connection(update_state->wrepl_in, nt_errstr(status));
338 }
339 
wreplsrv_in_update(struct wreplsrv_in_call * call)340 static NTSTATUS wreplsrv_in_update(struct wreplsrv_in_call *call)
341 {
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;
347 
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));
351 
352 	update_state = talloc(wrepl_in, struct wreplsrv_in_update_state);
353 	NT_STATUS_HAVE_NO_MEMORY(update_state);
354 
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);
363 
364 	if (wrepl_out->sock == NULL) {
365 		TALLOC_FREE(update_state);
366 		return NT_STATUS_NO_MEMORY;
367 	}
368 
369 	TALLOC_FREE(wrepl_in->send_queue);
370 
371 	status = wrepl_socket_donate_stream(wrepl_out->sock, &wrepl_in->tstream);
372 	if (!NT_STATUS_IS_OK(status)) {
373 		TALLOC_FREE(update_state);
374 		return status;
375 	}
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 		talloc_free(update_state);
387 		return NT_STATUS_INTERNAL_ERROR;
388 	}
389 
390 	update_state->creq->async.fn		= wreplsrv_in_update_handler;
391 	update_state->creq->async.private_data	= update_state;
392 
393 	return ERROR_INVALID_PARAMETER;
394 }
395 
wreplsrv_in_update2(struct wreplsrv_in_call * call)396 static NTSTATUS wreplsrv_in_update2(struct wreplsrv_in_call *call)
397 {
398 	return wreplsrv_in_update(call);
399 }
400 
wreplsrv_in_inform(struct wreplsrv_in_call * call)401 static NTSTATUS wreplsrv_in_inform(struct wreplsrv_in_call *call)
402 {
403 	struct wrepl_table *inform_in = &call->req_packet.message.replication.info.table;
404 
405 	DEBUG(2,("WREPL_REPL_INFORM: partner[%s] initiator[%s] num_owners[%u]\n",
406 		call->wreplconn->partner->address,
407 		inform_in->initiator, inform_in->partner_count));
408 
409 	wreplsrv_out_partner_pull(call->wreplconn->partner, inform_in);
410 
411 	/* we don't reply to WREPL_REPL_INFORM messages */
412 	return ERROR_INVALID_PARAMETER;
413 }
414 
wreplsrv_in_inform2(struct wreplsrv_in_call * call)415 static NTSTATUS wreplsrv_in_inform2(struct wreplsrv_in_call *call)
416 {
417 	return wreplsrv_in_inform(call);
418 }
419 
wreplsrv_in_replication(struct wreplsrv_in_call * call)420 static NTSTATUS wreplsrv_in_replication(struct wreplsrv_in_call *call)
421 {
422 	struct wrepl_replication *repl_in = &call->req_packet.message.replication;
423 	NTSTATUS status;
424 
425 	/*
426 	 * w2k only check the assoc_ctx if the opcode has the 0x00007800 bits are set
427 	 */
428 	if (call->req_packet.opcode & WREPL_OPCODE_BITS) {
429 		/*
430 		 *if the assoc_ctx doesn't match ignore the packet
431 		 */
432 		if (call->req_packet.assoc_ctx != call->wreplconn->assoc_ctx.our_ctx) {
433 			return ERROR_INVALID_PARAMETER;
434 		}
435 	}
436 
437 	if (!call->wreplconn->partner) {
438 		struct tsocket_address *peer_addr = call->wreplconn->conn->remote_address;
439 		char *peer_ip;
440 
441 		if (!tsocket_address_is_inet(peer_addr, "ipv4")) {
442 			DEBUG(0,("wreplsrv_in_replication: non ipv4 peer addr '%s'\n",
443 				tsocket_address_string(peer_addr, call)));
444 			return NT_STATUS_INTERNAL_ERROR;
445 		}
446 
447 		peer_ip = tsocket_address_inet_addr_string(peer_addr, call);
448 		if (peer_ip == NULL) {
449 			return NT_STATUS_NO_MEMORY;
450 		}
451 
452 		call->wreplconn->partner = wreplsrv_find_partner(call->wreplconn->service, peer_ip);
453 		if (!call->wreplconn->partner) {
454 			DEBUG(1,("Failing WINS replication from non-partner %s\n", peer_ip));
455 			return wreplsrv_in_stop_assoc_ctx(call);
456 		}
457 	}
458 
459 	switch (repl_in->command) {
460 		case WREPL_REPL_TABLE_QUERY:
461 			if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PUSH)) {
462 				DEBUG(0,("Failing WINS replication TABLE_QUERY from non-push-partner %s\n",
463 					 call->wreplconn->partner->address));
464 				return wreplsrv_in_stop_assoc_ctx(call);
465 			}
466 			status = wreplsrv_in_table_query(call);
467 			break;
468 
469 		case WREPL_REPL_TABLE_REPLY:
470 			return ERROR_INVALID_PARAMETER;
471 
472 		case WREPL_REPL_SEND_REQUEST:
473 			if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PUSH)) {
474 				DEBUG(0,("Failing WINS replication SEND_REQUESET from non-push-partner %s\n",
475 					 call->wreplconn->partner->address));
476 				return wreplsrv_in_stop_assoc_ctx(call);
477 			}
478 			status = wreplsrv_in_send_request(call);
479 			break;
480 
481 		case WREPL_REPL_SEND_REPLY:
482 			return ERROR_INVALID_PARAMETER;
483 
484 		case WREPL_REPL_UPDATE:
485 			if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
486 				DEBUG(0,("Failing WINS replication UPDATE from non-pull-partner %s\n",
487 					 call->wreplconn->partner->address));
488 				return wreplsrv_in_stop_assoc_ctx(call);
489 			}
490 			status = wreplsrv_in_update(call);
491 			break;
492 
493 		case WREPL_REPL_UPDATE2:
494 			if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
495 				DEBUG(0,("Failing WINS replication UPDATE2 from non-pull-partner %s\n",
496 					 call->wreplconn->partner->address));
497 				return wreplsrv_in_stop_assoc_ctx(call);
498 			}
499 			status = wreplsrv_in_update2(call);
500 			break;
501 
502 		case WREPL_REPL_INFORM:
503 			if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
504 				DEBUG(0,("Failing WINS replication INFORM from non-pull-partner %s\n",
505 					 call->wreplconn->partner->address));
506 				return wreplsrv_in_stop_assoc_ctx(call);
507 			}
508 			status = wreplsrv_in_inform(call);
509 			break;
510 
511 		case WREPL_REPL_INFORM2:
512 			if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
513 				DEBUG(0,("Failing WINS replication INFORM2 from non-pull-partner %s\n",
514 					 call->wreplconn->partner->address));
515 				return wreplsrv_in_stop_assoc_ctx(call);
516 			}
517 			status = wreplsrv_in_inform2(call);
518 			break;
519 
520 		default:
521 			return ERROR_INVALID_PARAMETER;
522 	}
523 
524 	if (NT_STATUS_IS_OK(status)) {
525 		call->rep_packet.mess_type = WREPL_REPLICATION;
526 	}
527 
528 	return status;
529 }
530 
wreplsrv_in_invalid_assoc_ctx(struct wreplsrv_in_call * call)531 static NTSTATUS wreplsrv_in_invalid_assoc_ctx(struct wreplsrv_in_call *call)
532 {
533 	struct wrepl_start *start	= &call->rep_packet.message.start;
534 
535 	call->rep_packet.opcode		= 0x00008583;
536 	call->rep_packet.assoc_ctx	= 0;
537 	call->rep_packet.mess_type	= WREPL_START_ASSOCIATION;
538 
539 	start->assoc_ctx		= 0x0000000a;
540 	start->minor_version		= 0x0001;
541 	start->major_version		= 0x0000;
542 
543 	call->rep_packet.padding	= data_blob_talloc(call, NULL, 4);
544 	memset(call->rep_packet.padding.data, '\0', call->rep_packet.padding.length);
545 
546 	return NT_STATUS_OK;
547 }
548 
wreplsrv_in_call(struct wreplsrv_in_call * call)549 NTSTATUS wreplsrv_in_call(struct wreplsrv_in_call *call)
550 {
551 	NTSTATUS status;
552 
553 	if (!(call->req_packet.opcode & WREPL_OPCODE_BITS)
554 	    && (call->wreplconn->assoc_ctx.our_ctx == WREPLSRV_INVALID_ASSOC_CTX)) {
555 		return wreplsrv_in_invalid_assoc_ctx(call);
556 	}
557 
558 	switch (call->req_packet.mess_type) {
559 		case WREPL_START_ASSOCIATION:
560 			status = wreplsrv_in_start_association(call);
561 			break;
562 		case WREPL_START_ASSOCIATION_REPLY:
563 			/* this is not valid here, so we ignore it */
564 			return ERROR_INVALID_PARAMETER;
565 
566 		case WREPL_STOP_ASSOCIATION:
567 			status = wreplsrv_in_stop_association(call);
568 			break;
569 
570 		case WREPL_REPLICATION:
571 			status = wreplsrv_in_replication(call);
572 			break;
573 		default:
574 			/* everythingelse is also not valid here, so we ignore it */
575 			return ERROR_INVALID_PARAMETER;
576 	}
577 
578 	if (call->wreplconn->assoc_ctx.our_ctx == WREPLSRV_INVALID_ASSOC_CTX) {
579 		return wreplsrv_in_invalid_assoc_ctx(call);
580 	}
581 
582 	if (NT_STATUS_IS_OK(status)) {
583 		/* let the backend to set some of the opcode bits, but always add the standards */
584 		call->rep_packet.opcode		|= WREPL_OPCODE_BITS;
585 		call->rep_packet.assoc_ctx	= call->wreplconn->assoc_ctx.peer_ctx;
586 	}
587 
588 	return status;
589 }
590