r12608: Remove some unused #include lines.
[kamenim/samba.git] / source4 / wrepl_server / wrepl_in_call.c
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 "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"
34
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         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;
56         }
57
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;
61
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;
66
67         return NT_STATUS_OK;
68 }
69
70 static NTSTATUS wreplsrv_in_stop_assoc_ctx(struct wreplsrv_in_call *call)
71 {
72         struct wrepl_stop *stop_out             = &call->rep_packet.message.stop;
73
74         call->wreplconn->assoc_ctx.stopped      = True;
75
76         call->rep_packet.mess_type              = WREPL_STOP_ASSOCIATION;
77         stop_out->reason                        = 4;
78
79         return NT_STATUS_OK;
80 }
81
82 static NTSTATUS wreplsrv_in_stop_association(struct wreplsrv_in_call *call)
83 {
84         /*
85          * w2k only check the assoc_ctx if the opcode has the 0x00007800 bits are set
86          */
87         if (call->req_packet.opcode & WREPL_OPCODE_BITS) {
88                 /*
89                  *if the assoc_ctx doesn't match ignore the packet
90                  */
91                 if (call->req_packet.assoc_ctx != call->wreplconn->assoc_ctx.our_ctx) {
92                         return ERROR_INVALID_PARAMETER;
93                 }
94                 /* when the opcode bits are set the connection should be directly terminated */
95                 return NT_STATUS_CONNECTION_RESET;
96         }
97
98         if (call->wreplconn->assoc_ctx.stopped) {
99                 /* this causes the connection to be directly terminated */
100                 return NT_STATUS_CONNECTION_RESET;
101         }
102
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);
106 }
107
108 static NTSTATUS wreplsrv_in_table_query(struct wreplsrv_in_call *call)
109 {
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;
114
115         repl_out->command = WREPL_REPL_TABLE_REPLY;
116
117         return wreplsrv_fill_wrepl_table(service, call, table_out,
118                                          our_ip, our_ip, True);
119 }
120
121 static int wreplsrv_in_sort_wins_name(struct wrepl_wins_name *n1,
122                                       struct wrepl_wins_name *n2)
123 {
124         if (n1->id < n2->id) return -1;
125         if (n1->id > n2->id) return 1;
126         return 0;
127 }
128
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)
133 {
134         uint32_t num_ips, i;
135         struct wrepl_ip *ips;
136
137         name->name              = rec->name;
138         talloc_steal(mem_ctx, rec->name);
139
140         name->id                = rec->version;
141         name->unknown           = WINSDB_GROUP_ADDRESS;
142
143         name->flags             = WREPL_NAME_FLAGS(rec->type, rec->state, rec->node, rec->is_static);
144
145         switch (name->flags & 2) {
146         case 0:
147                 name->addresses.ip                      = rec->addresses[0]->address;
148                 talloc_steal(mem_ctx, rec->addresses[0]->address);
149                 break;
150         case 2:
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);
154
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);
159                         } else {
160                                 ips[i].owner    = rec->addresses[i]->wins_owner;
161                                 talloc_steal(ips, rec->addresses[i]->wins_owner);
162                         }
163                         ips[i].ip       = rec->addresses[i]->address;
164                         talloc_steal(ips, rec->addresses[i]->address);
165                 }
166
167                 name->addresses.addresses.num_ips       = num_ips;
168                 name->addresses.addresses.ips           = ips;
169                 break;
170         }
171
172         return NT_STATUS_OK;
173 }
174
175 static NTSTATUS wreplsrv_in_send_request(struct wreplsrv_in_call *call)
176 {
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;
183         const char *filter;
184         struct ldb_result *res = NULL;
185         int ret;
186         struct wrepl_wins_name *names;
187         struct winsdb_record *rec;
188         NTSTATUS status;
189         uint32_t i;
190
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;
198         } else {
199                 owner = wreplsrv_find_owner(service->table, owner_in->address);
200         }
201
202         repl_out->command       = WREPL_REPL_SEND_REPLY;
203         reply_out->num_names    = 0;
204         reply_out->names        = NULL;
205
206         /*
207          * if we didn't know this owner, must be a bug in the partners client code...
208          * return an empty list.
209          */
210         if (!owner) {
211                 return NT_STATUS_OK;
212         }
213
214         /*
215          * the client sends a max_version of 0, interpret it as
216          * (uint64_t)-1
217          */
218         if (owner_in->max_version == 0) {
219                 owner_in->max_version = (uint64_t)-1;
220         }
221
222         /*
223          * if the partner ask for nothing, or give invalid ranges,
224          * return an empty list.
225          */
226         if (owner_in->min_version > owner_in->max_version) {
227                 return NT_STATUS_OK;
228         }
229
230         /*
231          * if the partner has already all records for nothing, or give invalid ranges,
232          * return an empty list.
233          */
234         if (owner_in->min_version > owner->owner.max_version) {
235                 return NT_STATUS_OK;
236         }
237
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));
256                 return NT_STATUS_OK;
257         }
258
259         names = talloc_array(call, struct wrepl_wins_name, res->count);
260         NT_STATUS_HAVE_NO_MEMORY(names);
261
262         for (i = 0; i < res->count; i++) {
263                 status = winsdb_record(res->msgs[i], call, &rec);
264                 NT_STATUS_NOT_OK_RETURN(status);
265
266                 status = wreplsrv_record2wins_name(names, call->wreplconn->our_ip, &names[i], rec);
267                 NT_STATUS_NOT_OK_RETURN(status);
268                 talloc_free(rec);
269                 talloc_free(res->msgs[i]);
270         }
271
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);
274
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));
280
281         reply_out->num_names    = res->count;
282         reply_out->names        = names;
283
284         return NT_STATUS_OK;
285 }
286
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;
292 };
293
294 static void wreplsrv_in_update_handler(struct composite_context *creq)
295 {
296         struct wreplsrv_in_update_state *update_state = talloc_get_type(creq->async.private_data,
297                                                         struct wreplsrv_in_update_state);
298         NTSTATUS status;
299
300         status = wreplsrv_pull_cycle_recv(creq);
301
302         talloc_free(update_state->wrepl_out);
303
304         wreplsrv_terminate_in_connection(update_state->wrepl_in, nt_errstr(status));
305 }
306
307 static NTSTATUS wreplsrv_in_update(struct wreplsrv_in_call *call)
308 {
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;
313         uint16_t fde_flags;
314
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));
318
319         /* 
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
323          */
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;
327
328         update_state = talloc(wrepl_in, struct wreplsrv_in_update_state);
329         NT_STATUS_HAVE_NO_MEMORY(update_state);
330
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,
340                                                              wrepl_in->packet);
341         NT_STATUS_HAVE_NO_MEMORY(wrepl_out->sock);
342
343         event_set_fd_flags(wrepl_out->sock->event.fde, fde_flags);
344
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;
355         }
356
357         update_state->creq->async.fn            = wreplsrv_in_update_handler;
358         update_state->creq->async.private_data  = update_state;
359
360         return ERROR_INVALID_PARAMETER;
361 }
362
363 static NTSTATUS wreplsrv_in_update2(struct wreplsrv_in_call *call)
364 {
365         return wreplsrv_in_update(call);
366 }
367
368 static NTSTATUS wreplsrv_in_inform(struct wreplsrv_in_call *call)
369 {
370         struct wrepl_table *inform_in = &call->req_packet.message.replication.info.table;
371
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));
375
376         wreplsrv_out_partner_pull(call->wreplconn->partner, inform_in);
377
378         /* we don't reply to WREPL_REPL_INFORM messages */
379         return ERROR_INVALID_PARAMETER;
380 }
381
382 static NTSTATUS wreplsrv_in_inform2(struct wreplsrv_in_call *call)
383 {
384         return wreplsrv_in_inform(call);
385 }
386
387 static NTSTATUS wreplsrv_in_replication(struct wreplsrv_in_call *call)
388 {
389         struct wrepl_replication *repl_in = &call->req_packet.message.replication;
390         NTSTATUS status;
391
392         /*
393          * w2k only check the assoc_ctx if the opcode has the 0x00007800 bits are set
394          */
395         if (call->req_packet.opcode & WREPL_OPCODE_BITS) {
396                 /*
397                  *if the assoc_ctx doesn't match ignore the packet
398                  */
399                 if (call->req_packet.assoc_ctx != call->wreplconn->assoc_ctx.our_ctx) {
400                         return ERROR_INVALID_PARAMETER;
401                 }
402         }
403
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);
408         }
409
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);
414                         }
415                         status = wreplsrv_in_table_query(call);
416                         break;
417
418                 case WREPL_REPL_TABLE_REPLY:
419                         return ERROR_INVALID_PARAMETER;
420
421                 case WREPL_REPL_SEND_REQUEST:
422                         if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PUSH)) {
423                                 return wreplsrv_in_stop_assoc_ctx(call);
424                         }
425                         status = wreplsrv_in_send_request(call);
426                         break;
427
428                 case WREPL_REPL_SEND_REPLY:
429                         return ERROR_INVALID_PARAMETER;
430         
431                 case WREPL_REPL_UPDATE:
432                         if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
433                                 return wreplsrv_in_stop_assoc_ctx(call);
434                         }
435                         status = wreplsrv_in_update(call);
436                         break;
437
438                 case WREPL_REPL_UPDATE2:
439                         if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
440                                 return wreplsrv_in_stop_assoc_ctx(call);
441                         }
442                         status = wreplsrv_in_update2(call);
443                         break;
444
445                 case WREPL_REPL_INFORM:
446                         if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
447                                 return wreplsrv_in_stop_assoc_ctx(call);
448                         }
449                         status = wreplsrv_in_inform(call);
450                         break;
451
452                 case WREPL_REPL_INFORM2:
453                         if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
454                                 return wreplsrv_in_stop_assoc_ctx(call);
455                         }
456                         status = wreplsrv_in_inform2(call);
457                         break;
458
459                 default:
460                         return ERROR_INVALID_PARAMETER;
461         }
462
463         if (NT_STATUS_IS_OK(status)) {
464                 call->rep_packet.mess_type = WREPL_REPLICATION;
465         }
466
467         return status;
468 }
469
470 static NTSTATUS wreplsrv_in_invalid_assoc_ctx(struct wreplsrv_in_call *call)
471 {
472         struct wrepl_start *start       = &call->rep_packet.message.start;
473
474         call->rep_packet.opcode         = 0x00008583;
475         call->rep_packet.assoc_ctx      = 0;
476         call->rep_packet.mess_type      = WREPL_START_ASSOCIATION;
477
478         start->assoc_ctx                = 0x0000000a;
479         start->minor_version            = 0x0001;
480         start->major_version            = 0x0000;
481
482         call->rep_packet.padding        = data_blob_talloc(call, NULL, 4);
483         memset(call->rep_packet.padding.data, '\0', call->rep_packet.padding.length);
484
485         return NT_STATUS_OK;
486 }
487
488 NTSTATUS wreplsrv_in_call(struct wreplsrv_in_call *call)
489 {
490         NTSTATUS status;
491
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);
495         }
496
497         switch (call->req_packet.mess_type) {
498                 case WREPL_START_ASSOCIATION:
499                         status = wreplsrv_in_start_association(call);
500                         break;
501                 case WREPL_START_ASSOCIATION_REPLY:
502                         /* this is not valid here, so we ignore it */
503                         return ERROR_INVALID_PARAMETER;
504
505                 case WREPL_STOP_ASSOCIATION:
506                         status = wreplsrv_in_stop_association(call);
507                         break;
508
509                 case WREPL_REPLICATION:
510                         status = wreplsrv_in_replication(call);
511                         break;
512                 default:
513                         /* everythingelse is also not valid here, so we ignore it */
514                         return ERROR_INVALID_PARAMETER;
515         }
516
517         if (call->wreplconn->assoc_ctx.our_ctx == WREPLSRV_INVALID_ASSOC_CTX) {
518                 return wreplsrv_in_invalid_assoc_ctx(call);
519         }
520
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;
525         }
526
527         return status;
528 }