r11033: r10343@SERNOX: metze | 2005-09-20 11:03:20 +0200
[tprouty/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 "dlinklist.h"
25 #include "lib/events/events.h"
26 #include "lib/socket/socket.h"
27 #include "smbd/service_task.h"
28 #include "smbd/service_stream.h"
29 #include "lib/messaging/irpc.h"
30 #include "librpc/gen_ndr/ndr_winsrepl.h"
31 #include "wrepl_server/wrepl_server.h"
32 #include "nbt_server/wins/winsdb.h"
33
34 static 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         if (start->minor_version != 2 || start->major_version != 5) {
53                 /* w2k terminate the connection if the versions doesn't match */
54                 return NT_STATUS_UNKNOWN_REVISION;
55         }
56
57         call->wreplconn->assoc_ctx.stopped      = False;
58         call->wreplconn->assoc_ctx.our_ctx      = WREPLSRV_VALID_ASSOC_CTX;
59         call->wreplconn->assoc_ctx.peer_ctx     = start->assoc_ctx;
60
61         call->rep_packet.mess_type              = WREPL_START_ASSOCIATION_REPLY;
62         start_reply->assoc_ctx                  = call->wreplconn->assoc_ctx.our_ctx;
63         start_reply->minor_version              = 2;
64         start_reply->major_version              = 5;
65
66         return NT_STATUS_OK;
67 }
68
69 static NTSTATUS wreplsrv_in_stop_assoc_ctx(struct wreplsrv_in_call *call)
70 {
71         struct wrepl_stop *stop_out             = &call->rep_packet.message.stop;
72
73         call->wreplconn->assoc_ctx.stopped      = True;
74
75         call->rep_packet.mess_type              = WREPL_STOP_ASSOCIATION;
76         stop_out->reason                        = 4;
77
78         return NT_STATUS_OK;
79 }
80
81 static NTSTATUS wreplsrv_in_stop_association(struct wreplsrv_in_call *call)
82 {
83         /*
84          * w2k only check the assoc_ctx if the opcode has the 0x00007800 bits are set
85          */
86         if (call->req_packet.opcode & WREPL_OPCODE_BITS) {
87                 /*
88                  *if the assoc_ctx doesn't match ignore the packet
89                  */
90                 if (call->req_packet.assoc_ctx != call->wreplconn->assoc_ctx.our_ctx) {
91                         return ERROR_INVALID_PARAMETER;
92                 }
93                 /* when the opcode bits are set the connection should be directly terminated */
94                 return NT_STATUS_CONNECTION_RESET;
95         }
96
97         if (call->wreplconn->assoc_ctx.stopped) {
98                 /* this causes the connection to be directly terminated */
99                 return NT_STATUS_CONNECTION_RESET;
100         }
101
102         /* this will cause to not receive packets anymore and terminate the connection if the reply is send */
103         call->wreplconn->terminate = True;
104         return wreplsrv_in_stop_assoc_ctx(call);
105 }
106
107 static NTSTATUS wreplsrv_in_table_query(struct wreplsrv_in_call *call)
108 {
109         struct wreplsrv_service *service = call->wreplconn->service;
110         struct wrepl_replication *repl_out = &call->rep_packet.message.replication;
111         struct wrepl_table *table_out = &call->rep_packet.message.replication.info.table;
112         struct wreplsrv_owner *cur;
113         uint64_t local_max_version;
114         uint32_t i = 0;
115
116         repl_out->command = WREPL_REPL_TABLE_REPLY;
117
118         table_out->partner_count        = 0;
119         table_out->partners             = NULL;
120         table_out->initiator            = WINSDB_OWNER_LOCAL;
121
122         local_max_version = wreplsrv_local_max_version(service);
123         if (local_max_version > 0) {
124                 table_out->partner_count++;
125         }
126
127         for (cur = service->table; cur; cur = cur->next) {
128                 table_out->partner_count++;
129         }
130
131         table_out->partners = talloc_array(call, struct wrepl_wins_owner, table_out->partner_count);
132         NT_STATUS_HAVE_NO_MEMORY(table_out->partners);
133
134         if (local_max_version > 0) {
135                 table_out->partners[i].address          = call->wreplconn->our_ip;
136                 table_out->partners[i].min_version      = 0;
137                 table_out->partners[i].max_version      = local_max_version;
138                 table_out->partners[i].type             = 1;
139                 i++;
140         }
141
142         for (cur = service->table; cur; cur = cur->next) {
143                 table_out->partners[i] = cur->owner;
144                 i++;
145         }
146
147         return NT_STATUS_OK;
148 }
149
150 static NTSTATUS wreplsrv_in_send_request(struct wreplsrv_in_call *call)
151 {
152         struct wrepl_replication *repl_out = &call->rep_packet.message.replication;
153         struct wrepl_send_reply *reply_out = &call->rep_packet.message.replication.info.reply;
154
155         repl_out->command = WREPL_REPL_SEND_REPLY;
156
157         reply_out->num_names    = 0;
158         reply_out->names        = NULL;
159
160         return NT_STATUS_OK;
161 }
162
163 static NTSTATUS wreplsrv_in_replication(struct wreplsrv_in_call *call)
164 {
165         struct wrepl_replication *repl_in = &call->req_packet.message.replication;
166         NTSTATUS status;
167
168         /*
169          * w2k only check the assoc_ctx if the opcode has the 0x00007800 bits are set
170          */
171         if (call->req_packet.opcode & WREPL_OPCODE_BITS) {
172                 /*
173                  *if the assoc_ctx doesn't match ignore the packet
174                  */
175                 if (call->req_packet.assoc_ctx != call->wreplconn->assoc_ctx.our_ctx) {
176                         return ERROR_INVALID_PARAMETER;
177                 }
178         }
179
180         if (!call->wreplconn->partner) {
181                 return wreplsrv_in_stop_assoc_ctx(call);
182         }
183
184         switch (repl_in->command) {
185                 case WREPL_REPL_TABLE_QUERY:
186                         status = wreplsrv_in_table_query(call);
187                         break;
188
189                 case WREPL_REPL_TABLE_REPLY:
190                         return ERROR_INVALID_PARAMETER;
191
192                 case WREPL_REPL_SEND_REQUEST:
193                         status = wreplsrv_in_send_request(call);
194                         break;
195
196                 case WREPL_REPL_SEND_REPLY:
197                         return ERROR_INVALID_PARAMETER;
198         
199                 case WREPL_REPL_UPDATE:
200                         return ERROR_INVALID_PARAMETER;
201
202                 case WREPL_REPL_5:
203                         return ERROR_INVALID_PARAMETER;
204
205                 case WREPL_REPL_INFORM:
206                         return ERROR_INVALID_PARAMETER;
207
208                 case WREPL_REPL_9:
209                         return ERROR_INVALID_PARAMETER;
210
211                 default:
212                         return ERROR_INVALID_PARAMETER;
213         }
214
215         if (NT_STATUS_IS_OK(status)) {
216                 call->rep_packet.mess_type = WREPL_REPLICATION;
217         }
218
219         return status;
220 }
221
222 static NTSTATUS wreplsrv_in_invalid_assoc_ctx(struct wreplsrv_in_call *call)
223 {
224         struct wrepl_start *start       = &call->rep_packet.message.start;
225
226         call->rep_packet.opcode         = 0x00008583;
227         call->rep_packet.assoc_ctx      = 0;
228         call->rep_packet.mess_type      = WREPL_START_ASSOCIATION;
229
230         start->assoc_ctx                = 0x0000000a;
231         start->minor_version            = 0x0001;
232         start->major_version            = 0x0000;
233
234         call->rep_packet.padding        = data_blob_talloc(call, NULL, 4);
235         memset(call->rep_packet.padding.data, '\0', call->rep_packet.padding.length);
236
237         return NT_STATUS_OK;
238 }
239
240 NTSTATUS wreplsrv_in_call(struct wreplsrv_in_call *call)
241 {
242         NTSTATUS status;
243
244         if (!(call->req_packet.opcode & WREPL_OPCODE_BITS)
245             && (call->wreplconn->assoc_ctx.our_ctx == WREPLSRV_INVALID_ASSOC_CTX)) {
246                 return wreplsrv_in_invalid_assoc_ctx(call);
247         }
248
249         switch (call->req_packet.mess_type) {
250                 case WREPL_START_ASSOCIATION:
251                         status = wreplsrv_in_start_association(call);
252                         break;
253                 case WREPL_START_ASSOCIATION_REPLY:
254                         /* this is not valid here, so we ignore it */
255                         return ERROR_INVALID_PARAMETER;
256
257                 case WREPL_STOP_ASSOCIATION:
258                         status = wreplsrv_in_stop_association(call);
259                         break;
260
261                 case WREPL_REPLICATION:
262                         status = wreplsrv_in_replication(call);
263                         break;
264                 default:
265                         /* everythingelse is also not valid here, so we ignore it */
266                         return ERROR_INVALID_PARAMETER;
267         }
268
269         if (call->wreplconn->assoc_ctx.our_ctx == WREPLSRV_INVALID_ASSOC_CTX) {
270                 return wreplsrv_in_invalid_assoc_ctx(call);
271         }
272
273         if (NT_STATUS_IS_OK(status)) {
274                 call->rep_packet.opcode         = WREPL_OPCODE_BITS;
275                 call->rep_packet.assoc_ctx      = call->wreplconn->assoc_ctx.peer_ctx;
276         }
277
278         return status;
279 }