r11908: implement SGROUP merging, that passes the different owner tests
[kai/samba.git] / source4 / wrepl_server / wrepl_apply_records.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 "wrepl_server/wrepl_out_helpers.h"
33 #include "nbt_server/wins/winsdb.h"
34 #include "ldb/include/ldb.h"
35 #include "libcli/composite/composite.h"
36 #include "libcli/wrepl/winsrepl.h"
37 #include "system/time.h"
38
39 enum _R_ACTION {
40         R_INVALID,
41         R_DO_REPLACE,
42         R_NOT_REPLACE,
43         R_DO_CHALLENGE,
44         R_DO_RELEASE_DEMAND,
45         R_DO_SGROUP_MERGE
46 };
47
48 static const char *_R_ACTION_enum_string(enum _R_ACTION action)
49 {
50         switch (action) {
51         case R_INVALID:                 return "INVALID";
52         case R_DO_REPLACE:              return "REPLACE";
53         case R_NOT_REPLACE:             return "NOT_REPLACE";
54         case R_DO_CHALLENGE:            return "CHALLEGNE";
55         case R_DO_RELEASE_DEMAND:       return "RELEASE_DEMAND";
56         case R_DO_SGROUP_MERGE:         return "SGROUP_MERGE";
57         }
58
59         return "enum _R_ACTION unknown";
60 }
61
62 #define R_IS_ACTIVE(r) ((r)->state == WREPL_STATE_ACTIVE)
63 #define R_IS_RELEASED(r) ((r)->state == WREPL_STATE_RELEASED)
64 #define R_IS_TOMBSTONE(r) ((r)->state == WREPL_STATE_TOMBSTONE)
65
66 #define R_IS_UNIQUE(r) ((r)->type == WREPL_TYPE_UNIQUE)
67 #define R_IS_GROUP(r) ((r)->type == WREPL_TYPE_GROUP)
68 #define R_IS_SGROUP(r) ((r)->type == WREPL_TYPE_SGROUP)
69 #define R_IS_MHOMED(r) ((r)->type == WREPL_TYPE_MHOMED)
70
71 /* blindly overwrite records from the same owner in all cases */
72 static enum _R_ACTION replace_same_owner(struct winsdb_record *r1, struct wrepl_name *r2)
73 {
74         /* REPLACE */
75         return R_DO_REPLACE;
76 }
77
78 static BOOL r_1_is_subset_of_2_address_list(struct winsdb_record *r1, struct wrepl_name *r2, BOOL check_owners)
79 {
80         uint32_t i,j;
81         size_t len = winsdb_addr_list_length(r1->addresses);
82
83         for (i=0; i < len; i++) {
84                 BOOL found = False;
85                 for (j=0; j < r2->num_addresses; j++) {
86                         if (strcmp(r1->addresses[i]->address, r2->addresses[j].address) != 0) {
87                                 continue;
88                         }
89
90                         if (check_owners && strcmp(r1->addresses[i]->wins_owner, r2->addresses[j].owner) != 0) {
91                                 return False;
92                         }
93                         found = True;
94                         break;
95                 }
96                 if (!found) return False;
97         }
98
99         return True;
100 }
101
102 static BOOL r_1_is_superset_of_2_address_list(struct winsdb_record *r1, struct wrepl_name *r2, BOOL check_owners)
103 {
104         uint32_t i,j;
105         size_t len = winsdb_addr_list_length(r1->addresses);
106
107         for (i=0; i < r2->num_addresses; i++) {
108                 BOOL found = False;
109                 for (j=0; j < len; j++) {
110                         if (strcmp(r2->addresses[i].address, r1->addresses[j]->address) != 0) {
111                                 continue;
112                         }
113
114                         if (check_owners && strcmp(r2->addresses[i].owner, r1->addresses[j]->wins_owner) != 0) {
115                                 return False;
116                         }
117                         found = True;
118                         break;
119                 }
120                 if (!found) return False;
121         }
122
123         return True;
124 }
125
126 static BOOL r_1_is_same_as_2_address_list(struct winsdb_record *r1, struct wrepl_name *r2, BOOL check_owners)
127 {
128         size_t len = winsdb_addr_list_length(r1->addresses);
129
130         if (len != r2->num_addresses) {
131                 return False;
132         }
133
134         return r_1_is_superset_of_2_address_list(r1, r2, check_owners);
135 }
136
137 static BOOL r_contains_addrs_from_owner(struct winsdb_record *r1, const char *owner)
138 {
139         uint32_t i;
140         size_t len = winsdb_addr_list_length(r1->addresses);
141
142         for (i=0; i < len; i++) {
143                 if (strcmp(r1->addresses[i]->wins_owner, owner) == 0) {
144                         return True;
145                 }
146         }
147
148         return False;
149 }
150
151 /*
152 UNIQUE,ACTIVE vs. UNIQUE,ACTIVE with different ip(s) => REPLACE
153 UNIQUE,ACTIVE vs. UNIQUE,TOMBSTONE with different ip(s) => NOT REPLACE
154 UNIQUE,RELEASED vs. UNIQUE,ACTIVE with different ip(s) => REPLACE
155 UNIQUE,RELEASED vs. UNIQUE,TOMBSTONE with different ip(s) => REPLACE
156 UNIQUE,TOMBSTONE vs. UNIQUE,ACTIVE with different ip(s) => REPLACE
157 UNIQUE,TOMBSTONE vs. UNIQUE,TOMBSTONE with different ip(s) => REPLACE
158 UNIQUE,ACTIVE vs. GROUP,ACTIVE with different ip(s) => REPLACE
159 UNIQUE,ACTIVE vs. GROUP,TOMBSTONE with same ip(s) => NOT REPLACE
160 UNIQUE,RELEASED vs. GROUP,ACTIVE with different ip(s) => REPLACE
161 UNIQUE,RELEASED vs. GROUP,TOMBSTONE with different ip(s) => REPLACE
162 UNIQUE,TOMBSTONE vs. GROUP,ACTIVE with different ip(s) => REPLACE
163 UNIQUE,TOMBSTONE vs. GROUP,TOMBSTONE with different ip(s) => REPLACE
164 UNIQUE,ACTIVE vs. SGROUP,ACTIVE with same ip(s) => NOT REPLACE
165 UNIQUE,ACTIVE vs. SGROUP,TOMBSTONE with same ip(s) => NOT REPLACE
166 UNIQUE,RELEASED vs. SGROUP,ACTIVE with different ip(s) => REPLACE
167 UNIQUE,RELEASED vs. SGROUP,TOMBSTONE with different ip(s) => REPLACE
168 UNIQUE,TOMBSTONE vs. SGROUP,ACTIVE with different ip(s) => REPLACE
169 UNIQUE,TOMBSTONE vs. SGROUP,TOMBSTONE with different ip(s) => REPLACE
170 UNIQUE,ACTIVE vs. MHOMED,ACTIVE with different ip(s) => REPLACE
171 UNIQUE,ACTIVE vs. MHOMED,TOMBSTONE with same ip(s) => NOT REPLACE
172 UNIQUE,RELEASED vs. MHOMED,ACTIVE with different ip(s) => REPLACE
173 UNIQUE,RELEASED vs. MHOMED,TOMBSTONE with different ip(s) => REPLACE
174 UNIQUE,TOMBSTONE vs. MHOMED,ACTIVE with different ip(s) => REPLACE
175 UNIQUE,TOMBSTONE vs. MHOMED,TOMBSTONE with different ip(s) => REPLACE
176 */
177 static enum _R_ACTION replace_unique_replica_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
178 {
179         if (!R_IS_ACTIVE(r1)) {
180                 /* REPLACE */
181                 return R_DO_REPLACE;
182         }
183
184         if (!R_IS_SGROUP(r2) && R_IS_ACTIVE(r2)) {
185                 /* REPLACE */
186                 return R_DO_REPLACE;
187         }
188
189         /* NOT REPLACE */
190         return R_NOT_REPLACE;
191 }
192
193 /*
194 GROUP,ACTIVE vs. UNIQUE,ACTIVE with same ip(s) => NOT REPLACE
195 GROUP,ACTIVE vs. UNIQUE,TOMBSTONE with same ip(s) => NOT REPLACE
196 GROUP,RELEASED vs. UNIQUE,ACTIVE with same ip(s) => NOT REPLACE
197 GROUP,RELEASED vs. UNIQUE,TOMBSTONE with same ip(s) => NOT REPLACE
198 GROUP,TOMBSTONE vs. UNIQUE,ACTIVE with same ip(s) => NOT REPLACE
199 GROUP,TOMBSTONE vs. UNIQUE,TOMBSTONE with same ip(s) => NOT REPLACE
200 GROUP,ACTIVE vs. GROUP,ACTIVE with same ip(s) => NOT REPLACE
201 GROUP,ACTIVE vs. GROUP,TOMBSTONE with same ip(s) => NOT REPLACE
202 GROUP,RELEASED vs. GROUP,ACTIVE with different ip(s) => REPLACE
203 GROUP,RELEASED vs. GROUP,TOMBSTONE with different ip(s) => REPLACE
204 GROUP,TOMBSTONE vs. GROUP,ACTIVE with different ip(s) => REPLACE
205 GROUP,TOMBSTONE vs. GROUP,TOMBSTONE with different ip(s) => REPLACE
206 GROUP,ACTIVE vs. SGROUP,ACTIVE with same ip(s) => NOT REPLACE
207 GROUP,ACTIVE vs. SGROUP,TOMBSTONE with same ip(s) => NOT REPLACE
208 GROUP,RELEASED vs. SGROUP,ACTIVE with different ip(s) => REPLACE
209 GROUP,RELEASED vs. SGROUP,TOMBSTONE with same ip(s) => NOT REPLACE
210 GROUP,TOMBSTONE vs. SGROUP,ACTIVE with different ip(s) => REPLACE
211 GROUP,TOMBSTONE vs. SGROUP,TOMBSTONE with different ip(s) => REPLACE
212 GROUP,ACTIVE vs. MHOMED,ACTIVE with same ip(s) => NOT REPLACE
213 GROUP,ACTIVE vs. MHOMED,TOMBSTONE with same ip(s) => NOT REPLACE
214 GROUP,RELEASED vs. MHOMED,ACTIVE with same ip(s) => NOT REPLACE
215 GROUP,RELEASED vs. MHOMED,TOMBSTONE with same ip(s) => NOT REPLACE
216 GROUP,TOMBSTONE vs. MHOMED,ACTIVE with different ip(s) => REPLACE
217 GROUP,TOMBSTONE vs. MHOMED,TOMBSTONE with different ip(s) => REPLACE
218 */
219 static enum _R_ACTION replace_group_replica_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
220 {
221         if (!R_IS_ACTIVE(r1) && R_IS_GROUP(r2)) {
222                 /* REPLACE */
223                 return R_DO_REPLACE;
224         }
225
226         if (R_IS_TOMBSTONE(r1) && !R_IS_UNIQUE(r2)) {
227                 /* REPLACE */
228                 return R_DO_REPLACE;
229         }
230
231         /* NOT REPLACE */
232         return R_NOT_REPLACE;
233 }
234
235 /*
236 SGROUP,ACTIVE vs. UNIQUE,ACTIVE with same ip(s) => NOT REPLACE
237 SGROUP,ACTIVE vs. UNIQUE,TOMBSTONE with same ip(s) => NOT REPLACE
238 SGROUP,RELEASED vs. UNIQUE,ACTIVE with different ip(s) => REPLACE
239 SGROUP,RELEASED vs. UNIQUE,TOMBSTONE with different ip(s) => REPLACE
240 SGROUP,TOMBSTONE vs. UNIQUE,ACTIVE with different ip(s) => REPLACE
241 SGROUP,TOMBSTONE vs. UNIQUE,TOMBSTONE with different ip(s) => REPLACE
242 SGROUP,ACTIVE vs. GROUP,ACTIVE with same ip(s) => NOT REPLACE
243 SGROUP,ACTIVE vs. GROUP,TOMBSTONE with same ip(s) => NOT REPLACE
244 SGROUP,RELEASED vs. GROUP,ACTIVE with different ip(s) => REPLACE
245 SGROUP,RELEASED vs. GROUP,TOMBSTONE with different ip(s) => REPLACE
246 SGROUP,TOMBSTONE vs. GROUP,ACTIVE with different ip(s) => REPLACE
247 SGROUP,TOMBSTONE vs. GROUP,TOMBSTONE with different ip(s) => REPLACE
248 SGROUP,RELEASED vs. SGROUP,ACTIVE with different ip(s) => REPLACE
249 SGROUP,RELEASED vs. SGROUP,TOMBSTONE with different ip(s) => REPLACE
250 SGROUP,TOMBSTONE vs. SGROUP,ACTIVE with different ip(s) => REPLACE
251 SGROUP,TOMBSTONE vs. SGROUP,TOMBSTONE with different ip(s) => REPLACE
252 SGROUP,ACTIVE vs. MHOMED,ACTIVE with same ip(s) => NOT REPLACE
253 SGROUP,ACTIVE vs. MHOMED,TOMBSTONE with same ip(s) => NOT REPLACE
254 SGROUP,RELEASED vs. MHOMED,ACTIVE with different ip(s) => REPLACE
255 SGROUP,RELEASED vs. MHOMED,TOMBSTONE with different ip(s) => REPLACE
256 SGROUP,TOMBSTONE vs. MHOMED,ACTIVE with different ip(s) => REPLACE
257 SGROUP,TOMBSTONE vs. MHOMED,TOMBSTONE with different ip(s) => REPLACE
258
259 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:A_3_4 => NOT REPLACE
260 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:NULL => NOT REPLACE
261 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4_X_3_4 vs. B:A_3_4 => NOT REPLACE
262 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:B_3_4 vs. B:A_3_4 => REPLACE
263 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:A_3_4_OWNER_B => REPLACE
264 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4_OWNER_B vs. B:A_3_4 => REPLACE
265
266 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:B_3_4 => C:A_3_4_B_3_4 => SGROUP_MERGE
267 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:B_3_4_X_3_4 vs. B:A_3_4 => B:A_3_4_X_3_4 => SGROUP_MERGE
268 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:X_3_4 vs. B:A_3_4 => C:A_3_4_X_3_4 => SGROUP_MERGE
269 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4_X_3_4 vs. B:A_3_4_OWNER_B => B:A_3_4_OWNER_B_X_3_4 => SGROUP_MERGE
270 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:B_3_4_X_3_4 vs. B:B_3_4_X_1_2 => C:B_3_4_X_1_2_3_4 => SGROUP_MERGE
271 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:B_3_4_X_3_4 vs. B:NULL => B:X_3_4 => SGROUP_MERGE
272 */
273 static enum _R_ACTION replace_sgroup_replica_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
274 {
275         if (!R_IS_ACTIVE(r1)) {
276                 /* REPLACE */
277                 return R_DO_REPLACE;
278         }
279
280         if (!R_IS_SGROUP(r2) || !R_IS_ACTIVE(r2)) {
281                 /* NOT REPLACE */
282                 return R_NOT_REPLACE;
283         }
284
285         if (r2->num_addresses == 0) {
286                 if (r_contains_addrs_from_owner(r1, r2->owner)) {
287                         /* not handled here: MERGE */
288                         return R_DO_SGROUP_MERGE;
289                 }
290
291                 /* NOT REPLACE */
292                 return R_NOT_REPLACE;
293         }
294
295         if (r_1_is_superset_of_2_address_list(r1, r2, True)) {
296                 /* NOT REPLACE */
297                 return R_NOT_REPLACE;
298         }
299
300         if (r_1_is_same_as_2_address_list(r1, r2, False)) {
301                 /* REPLACE */
302                 return R_DO_REPLACE;
303         }
304
305         /* not handled here: MERGE */
306         return R_DO_SGROUP_MERGE;
307 }
308
309 /*
310 MHOMED,ACTIVE vs. UNIQUE,ACTIVE with different ip(s) => REPLACE
311 MHOMED,ACTIVE vs. UNIQUE,TOMBSTONE with same ip(s) => NOT REPLACE
312 MHOMED,RELEASED vs. UNIQUE,ACTIVE with different ip(s) => REPLACE
313 MHOMED,RELEASED vs. UNIQUE,TOMBSTONE with different ip(s) => REPLACE
314 MHOMED,TOMBSTONE vs. UNIQUE,ACTIVE with different ip(s) => REPLACE
315 MHOMED,TOMBSTONE vs. UNIQUE,TOMBSTONE with different ip(s) => REPLACE
316 MHOMED,ACTIVE vs. GROUP,ACTIVE with different ip(s) => REPLACE
317 MHOMED,ACTIVE vs. GROUP,TOMBSTONE with same ip(s) => NOT REPLACE
318 MHOMED,RELEASED vs. GROUP,ACTIVE with different ip(s) => REPLACE
319 MHOMED,RELEASED vs. GROUP,TOMBSTONE with different ip(s) => REPLACE
320 MHOMED,TOMBSTONE vs. GROUP,ACTIVE with different ip(s) => REPLACE
321 MHOMED,TOMBSTONE vs. GROUP,TOMBSTONE with different ip(s) => REPLACE
322 MHOMED,ACTIVE vs. SGROUP,ACTIVE with same ip(s) => NOT REPLACE
323 MHOMED,ACTIVE vs. SGROUP,TOMBSTONE with same ip(s) => NOT REPLACE
324 MHOMED,RELEASED vs. SGROUP,ACTIVE with different ip(s) => REPLACE
325 MHOMED,RELEASED vs. SGROUP,TOMBSTONE with different ip(s) => REPLACE
326 MHOMED,TOMBSTONE vs. SGROUP,ACTIVE with different ip(s) => REPLACE
327 MHOMED,TOMBSTONE vs. SGROUP,TOMBSTONE with different ip(s) => REPLACE
328 MHOMED,ACTIVE vs. MHOMED,ACTIVE with different ip(s) => REPLACE
329 MHOMED,ACTIVE vs. MHOMED,TOMBSTONE with same ip(s) => NOT REPLACE
330 MHOMED,RELEASED vs. MHOMED,ACTIVE with different ip(s) => REPLACE
331 MHOMED,RELEASED vs. MHOMED,TOMBSTONE with different ip(s) => REPLACE
332 MHOMED,TOMBSTONE vs. MHOMED,ACTIVE with different ip(s) => REPLACE
333 MHOMED,TOMBSTONE vs. MHOMED,TOMBSTONE with different ip(s) => REPLACE
334 */
335 static enum _R_ACTION replace_mhomed_replica_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
336 {
337         if (!R_IS_ACTIVE(r1)) {
338                 /* REPLACE */
339                 return R_DO_REPLACE;
340         }
341
342         if (!R_IS_SGROUP(r2) && R_IS_ACTIVE(r2)) {
343                 /* REPLACE */
344                 return R_DO_REPLACE;
345         }
346
347         /* NOT REPLACE */
348         return R_NOT_REPLACE;
349 }
350
351 /*
352 active:
353 _UA_UA_SI_U<00> => REPLACE
354 _UA_UA_DI_P<00> => NOT REPLACE
355 _UA_UA_DI_O<00> => NOT REPLACE
356 _UA_UA_DI_N<00> => REPLACE
357 _UA_UT_SI_U<00> => NOT REPLACE
358 _UA_UT_DI_U<00> => NOT REPLACE
359 _UA_GA_SI_R<00> => REPLACE
360 _UA_GA_DI_R<00> => REPLACE
361 _UA_GT_SI_U<00> => NOT REPLACE
362 _UA_GT_DI_U<00> => NOT REPLACE
363 _UA_SA_SI_R<00> => REPLACE
364 _UA_SA_DI_R<00> => REPLACE
365 _UA_ST_SI_U<00> => NOT REPLACE
366 _UA_ST_DI_U<00> => NOT REPLACE
367 _UA_MA_SI_U<00> => REPLACE
368 _UA_MA_SP_U<00> => REPLACE
369 _UA_MA_DI_P<00> => NOT REPLACE
370 _UA_MA_DI_O<00> => NOT REPLACE
371 _UA_MA_DI_N<00> => REPLACE
372 _UA_MT_SI_U<00> => NOT REPLACE
373 _UA_MT_DI_U<00> => NOT REPLACE
374 Test Replica vs. owned active: some more UNIQUE,MHOMED combinations
375 _UA_UA_DI_A<00> => MHOMED_MERGE
376 _UA_MA_DI_A<00> => MHOMED_MERGE
377
378 released:
379 _UR_UA_SI<00> => REPLACE
380 _UR_UA_DI<00> => REPLACE
381 _UR_UT_SI<00> => REPLACE
382 _UR_UT_DI<00> => REPLACE
383 _UR_GA_SI<00> => REPLACE
384 _UR_GA_DI<00> => REPLACE
385 _UR_GT_SI<00> => REPLACE
386 _UR_GT_DI<00> => REPLACE
387 _UR_SA_SI<00> => REPLACE
388 _UR_SA_DI<00> => REPLACE
389 _UR_ST_SI<00> => REPLACE
390 _UR_ST_DI<00> => REPLACE
391 _UR_MA_SI<00> => REPLACE
392 _UR_MA_DI<00> => REPLACE
393 _UR_MT_SI<00> => REPLACE
394 _UR_MT_DI<00> => REPLACE
395 */
396 static enum _R_ACTION replace_unique_owned_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
397 {
398         if (!R_IS_ACTIVE(r1)) {
399                 /* REPLACE */
400                 return R_DO_REPLACE;
401         }
402
403         if (!R_IS_ACTIVE(r2)) {
404                 /* NOT REPLACE */
405                 return R_NOT_REPLACE;
406         }
407
408         if (R_IS_GROUP(r2) || R_IS_SGROUP(r2)) {
409                 /* REPLACE and send a release demand to the old name owner */
410                 return R_DO_RELEASE_DEMAND;
411         }
412
413         /* 
414          * here we only have unique,active,owned vs.
415          * is unique,active,replica or mhomed,active,replica
416          */
417
418         if (r_1_is_subset_of_2_address_list(r1, r2, False)) {
419                 /* 
420                  * if r1 has a subset(or same) of the addresses of r2
421                  * <=>
422                  * if r2 has a superset(or same) of the addresses of r1
423                  *
424                  * then replace the record
425                  */
426                 return R_DO_REPLACE;
427         }
428
429         /*
430          * in any other case, we need to do
431          * a name request to the old name holder
432          * to see if it's still there...
433          */
434         return R_DO_CHALLENGE;
435 }
436
437 /*
438 active:
439 _GA_UA_SI_U<00> => NOT REPLACE
440 _GA_UA_DI_U<00> => NOT REPLACE
441 _GA_UT_SI_U<00> => NOT REPLACE
442 _GA_UT_DI_U<00> => NOT REPLACE
443 _GA_GA_SI_U<00> => REPLACE
444 _GA_GA_DI_U<00> => REPLACE
445 _GA_GT_SI_U<00> => NOT REPLACE
446 _GA_GT_DI_U<00> => NOT REPLACE
447 _GA_SA_SI_U<00> => NOT REPLACE
448 _GA_SA_DI_U<00> => NOT REPLACE
449 _GA_ST_SI_U<00> => NOT REPLACE
450 _GA_ST_DI_U<00> => NOT REPLACE
451 _GA_MA_SI_U<00> => NOT REPLACE
452 _GA_MA_DI_U<00> => NOT REPLACE
453 _GA_MT_SI_U<00> => NOT REPLACE
454 _GA_MT_DI_U<00> => NOT REPLACE
455
456 released:
457 _GR_UA_SI<00> => NOT REPLACE
458 _GR_UA_DI<00> => NOT REPLACE
459 _GR_UT_SI<00> => NOT REPLACE
460 _GR_UT_DI<00> => NOT REPLACE
461 _GR_GA_SI<00> => REPLACE
462 _GR_GA_DI<00> => REPLACE
463 _GR_GT_SI<00> => REPLACE
464 _GR_GT_DI<00> => REPLACE
465 _GR_SA_SI<00> => NOT REPLACE
466 _GR_SA_DI<00> => NOT REPLACE
467 _GR_ST_SI<00> => NOT REPLACE
468 _GR_ST_DI<00> => NOT REPLACE
469 _GR_MA_SI<00> => NOT REPLACE
470 _GR_MA_DI<00> => NOT REPLACE
471 _GR_MT_SI<00> => NOT REPLACE
472 _GR_MT_DI<00> => NOT REPLACE
473 */
474 static enum _R_ACTION replace_group_owned_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
475 {
476         if (R_IS_GROUP(r1) && R_IS_GROUP(r2)) {
477                 if (!R_IS_ACTIVE(r1) || R_IS_ACTIVE(r2)) {
478                         /* REPLACE */
479                         return R_DO_REPLACE;
480                 }
481         }
482
483         /* NOT REPLACE */
484         return R_NOT_REPLACE;
485 }
486
487 /*
488 active (not sgroup vs. sgroup yet!):
489 _SA_UA_SI_U<1c> => NOT REPLACE
490 _SA_UA_DI_U<1c> => NOT REPLACE
491 _SA_UT_SI_U<1c> => NOT REPLACE
492 _SA_UT_DI_U<1c> => NOT REPLACE
493 _SA_GA_SI_U<1c> => NOT REPLACE
494 _SA_GA_DI_U<1c> => NOT REPLACE
495 _SA_GT_SI_U<1c> => NOT REPLACE
496 _SA_GT_DI_U<1c> => NOT REPLACE
497 _SA_MA_SI_U<1c> => NOT REPLACE
498 _SA_MA_DI_U<1c> => NOT REPLACE
499 _SA_MT_SI_U<1c> => NOT REPLACE
500 _SA_MT_DI_U<1c> => NOT REPLACE
501
502 SGROUP,ACTIVE vs. SGROUP,* is not handled here!
503
504 released:
505 _SR_UA_SI<1c> => REPLACE
506 _SR_UA_DI<1c> => REPLACE
507 _SR_UT_SI<1c> => REPLACE
508 _SR_UT_DI<1c> => REPLACE
509 _SR_GA_SI<1c> => REPLACE
510 _SR_GA_DI<1c> => REPLACE
511 _SR_GT_SI<1c> => REPLACE
512 _SR_GT_DI<1c> => REPLACE
513 _SR_SA_SI<1c> => REPLACE
514 _SR_SA_DI<1c> => REPLACE
515 _SR_ST_SI<1c> => REPLACE
516 _SR_ST_DI<1c> => REPLACE
517 _SR_MA_SI<1c> => REPLACE
518 _SR_MA_DI<1c> => REPLACE
519 _SR_MT_SI<1c> => REPLACE
520 _SR_MT_DI<1c> => REPLACE
521 */
522 static enum _R_ACTION replace_sgroup_owned_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
523 {
524         if (!R_IS_ACTIVE(r1)) {
525                 /* REPLACE */
526                 return R_DO_REPLACE;
527         }
528
529         if (R_IS_SGROUP(r2)) {
530                 /* not handled here: MERGE */
531                 return R_DO_SGROUP_MERGE;
532         }
533
534         /* NOT REPLACE */
535         return R_NOT_REPLACE;
536 }
537
538 /*
539 active:
540 _MA_UA_SI_U<00> => REPLACE
541 _MA_UA_DI_P<00> => NOT REPLACE
542 _MA_UA_DI_O<00> => NOT REPLACE
543 _MA_UA_DI_N<00> => REPLACE
544 _MA_UT_SI_U<00> => NOT REPLACE
545 _MA_UT_DI_U<00> => NOT REPLACE
546 _MA_GA_SI_R<00> => REPLACE
547 _MA_GA_DI_R<00> => REPLACE
548 _MA_GT_SI_U<00> => NOT REPLACE
549 _MA_GT_DI_U<00> => NOT REPLACE
550 _MA_SA_SI_R<00> => REPLACE
551 _MA_SA_DI_R<00> => REPLACE
552 _MA_ST_SI_U<00> => NOT REPLACE
553 _MA_ST_DI_U<00> => NOT REPLACE
554 _MA_MA_SI_U<00> => REPLACE
555 _MA_MA_SP_U<00> => REPLACE
556 _MA_MA_DI_P<00> => NOT REPLACE
557 _MA_MA_DI_O<00> => NOT REPLACE
558 _MA_MA_DI_N<00> => REPLACE
559 _MA_MT_SI_U<00> => NOT REPLACE
560 _MA_MT_DI_U<00> => NOT REPLACE
561 Test Replica vs. owned active: some more MHOMED combinations
562 _MA_MA_SP_U<00> => REPLACE
563 _MA_MA_SM_U<00> => REPLACE
564 _MA_MA_SB_P<00> => MHOMED_MERGE
565 _MA_MA_SB_A<00> => MHOMED_MERGE
566 _MA_MA_SB_PRA<00> => NOT REPLACE
567 _MA_MA_SB_O<00> => NOT REPLACE
568 _MA_MA_SB_N<00> => REPLACE
569 Test Replica vs. owned active: some more UNIQUE,MHOMED combinations
570 _MA_UA_SB_P<00> => MHOMED_MERGE
571
572 released:
573 _MR_UA_SI<00> => REPLACE
574 _MR_UA_DI<00> => REPLACE
575 _MR_UT_SI<00> => REPLACE
576 _MR_UT_DI<00> => REPLACE
577 _MR_GA_SI<00> => REPLACE
578 _MR_GA_DI<00> => REPLACE
579 _MR_GT_SI<00> => REPLACE
580 _MR_GT_DI<00> => REPLACE
581 _MR_SA_SI<00> => REPLACE
582 _MR_SA_DI<00> => REPLACE
583 _MR_ST_SI<00> => REPLACE
584 _MR_ST_DI<00> => REPLACE
585 _MR_MA_SI<00> => REPLACE
586 _MR_MA_DI<00> => REPLACE
587 _MR_MT_SI<00> => REPLACE
588 _MR_MT_DI<00> => REPLACE
589 */
590 static enum _R_ACTION replace_mhomed_owned_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
591 {
592         if (!R_IS_ACTIVE(r1)) {
593                 /* REPLACE */
594                 return R_DO_REPLACE;
595         }
596
597         if (!R_IS_ACTIVE(r2)) {
598                 /* NOT REPLACE */
599                 return R_NOT_REPLACE;
600         }
601
602         if (R_IS_GROUP(r2) || R_IS_SGROUP(r2)) {
603                 /* REPLACE and send a release demand to the old name owner */
604                 return R_DO_RELEASE_DEMAND;
605         }
606
607         /* 
608          * here we only have mhomed,active,owned vs.
609          * is unique,active,replica or mhomed,active,replica
610          */
611
612         if (r_1_is_subset_of_2_address_list(r1, r2, False)) {
613                 /* 
614                  * if r1 has a subset(or same) of the addresses of r2
615                  * <=>
616                  * if r2 has a superset(or same) of the addresses of r1
617                  *
618                  * then replace the record
619                  */
620                 return R_DO_REPLACE;
621         }
622
623         /*
624          * in any other case, we need to do
625          * a name request to the old name holder
626          * to see if it's still there...
627          */
628         return R_DO_CHALLENGE;
629 }
630
631 static NTSTATUS r_do_add(struct wreplsrv_partner *partner,
632                          TALLOC_CTX *mem_ctx,
633                          struct wrepl_wins_owner *owner,
634                          struct wrepl_name *replica)
635 {
636         struct winsdb_record *rec;
637         uint32_t i;
638         uint8_t ret;
639
640         rec = talloc(mem_ctx, struct winsdb_record);
641         NT_STATUS_HAVE_NO_MEMORY(rec);
642
643         rec->name       = &replica->name;
644         rec->type       = replica->type;
645         rec->state      = replica->state;
646         rec->node       = replica->node;
647         rec->is_static  = replica->is_static;
648         rec->expire_time= time(NULL) + partner->service->config.verify_interval;
649         rec->version    = replica->version_id;
650         rec->wins_owner = replica->owner;
651         rec->addresses  = winsdb_addr_list_make(rec);
652         NT_STATUS_HAVE_NO_MEMORY(rec->addresses);
653         rec->registered_by = NULL;
654
655         for (i=0; i < replica->num_addresses; i++) {
656                 /* TODO: find out if rec->expire_time is correct here */
657                 rec->addresses = winsdb_addr_list_add(rec->addresses,
658                                                       replica->addresses[i].address,
659                                                       replica->addresses[i].owner,
660                                                       rec->expire_time);
661                 NT_STATUS_HAVE_NO_MEMORY(rec->addresses);
662         }
663
664         ret = winsdb_add(partner->service->wins_db, rec, 0);
665         if (ret != NBT_RCODE_OK) {
666                 DEBUG(0,("Failed to add record %s: %u\n",
667                         nbt_name_string(mem_ctx, &replica->name), ret));
668                 return NT_STATUS_FOOBAR;
669         }
670
671         DEBUG(4,("added record %s\n",
672                 nbt_name_string(mem_ctx, &replica->name)));
673
674         return NT_STATUS_OK;
675 }
676
677 static NTSTATUS r_do_replace(struct wreplsrv_partner *partner,
678                              TALLOC_CTX *mem_ctx,
679                              struct winsdb_record *rec,
680                              struct wrepl_wins_owner *owner,
681                              struct wrepl_name *replica)
682 {
683         uint32_t i;
684         uint8_t ret;
685
686         rec->name       = &replica->name;
687         rec->type       = replica->type;
688         rec->state      = replica->state;
689         rec->node       = replica->node;
690         rec->is_static  = replica->is_static;
691         rec->expire_time= time(NULL) + partner->service->config.verify_interval;
692         rec->version    = replica->version_id;
693         rec->wins_owner = replica->owner;
694         rec->addresses  = winsdb_addr_list_make(rec);
695         NT_STATUS_HAVE_NO_MEMORY(rec->addresses);
696         rec->registered_by = NULL;
697
698         for (i=0; i < replica->num_addresses; i++) {
699                 /* TODO: find out if rec->expire_time is correct here */
700                 rec->addresses = winsdb_addr_list_add(rec->addresses,
701                                                       replica->addresses[i].address,
702                                                       replica->addresses[i].owner,
703                                                       rec->expire_time);
704                 NT_STATUS_HAVE_NO_MEMORY(rec->addresses);
705         }
706
707         ret = winsdb_modify(partner->service->wins_db, rec, 0);
708         if (ret != NBT_RCODE_OK) {
709                 DEBUG(0,("Failed to replace record %s: %u\n",
710                         nbt_name_string(mem_ctx, &replica->name), ret));
711                 return NT_STATUS_FOOBAR;
712         }
713
714         DEBUG(4,("replaced record %s\n",
715                 nbt_name_string(mem_ctx, &replica->name)));
716
717         return NT_STATUS_OK;
718 }
719
720 static NTSTATUS r_not_replace(struct wreplsrv_partner *partner,
721                               TALLOC_CTX *mem_ctx,
722                               struct winsdb_record *rec,
723                               struct wrepl_wins_owner *owner,
724                               struct wrepl_name *replica)
725 {
726         DEBUG(4,("not replace record %s\n",
727                  nbt_name_string(mem_ctx, &replica->name)));
728         return NT_STATUS_OK;
729 }
730
731 static NTSTATUS r_do_challenge(struct wreplsrv_partner *partner,
732                                TALLOC_CTX *mem_ctx,
733                                struct winsdb_record *rec,
734                                struct wrepl_wins_owner *owner,
735                                struct wrepl_name *replica)
736 {
737         /* TODO: !!! */
738         DEBUG(0,("TODO: challenge record %s\n",
739                  nbt_name_string(mem_ctx, &replica->name)));
740         return NT_STATUS_OK;
741 }
742
743 static NTSTATUS r_do_release_demand(struct wreplsrv_partner *partner,
744                                     TALLOC_CTX *mem_ctx,
745                                     struct winsdb_record *rec,
746                                     struct wrepl_wins_owner *owner,
747                                     struct wrepl_name *replica)
748 {
749         NTSTATUS status;
750         struct winsdb_addr **addresses;
751
752         /*
753          * we need to get a reference to the old addresses,
754          * as we need to send a release demand to them after replacing the record
755          * and r_do_replace() will modify rec->addresses
756          */
757         addresses = rec->addresses;
758
759         status = r_do_replace(partner, mem_ctx, rec, owner, replica);
760         NT_STATUS_NOT_OK_RETURN(status);
761
762         /* TODO: !!! */
763         DEBUG(0,("TODO: send release demand for %s\n",
764                  nbt_name_string(mem_ctx, &replica->name)));
765         return NT_STATUS_OK;
766 }
767
768 /*
769 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:A_3_4 => NOT REPLACE
770 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:NULL => NOT REPLACE
771 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4_X_3_4 vs. B:A_3_4 => NOT REPLACE
772 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:B_3_4 vs. B:A_3_4 => REPLACE
773 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:A_3_4_OWNER_B => REPLACE
774 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4_OWNER_B vs. B:A_3_4 => REPLACE
775
776 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:B_3_4 => C:A_3_4_B_3_4 => SGROUP_MERGE
777 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:B_3_4_X_3_4 vs. B:A_3_4 => B:A_3_4_X_3_4 => SGROUP_MERGE
778 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:X_3_4 vs. B:A_3_4 => C:A_3_4_X_3_4 => SGROUP_MERGE
779 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4_X_3_4 vs. B:A_3_4_OWNER_B => B:A_3_4_OWNER_B_X_3_4 => SGROUP_MERGE
780 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:B_3_4_X_3_4 vs. B:B_3_4_X_1_2 => C:B_3_4_X_1_2_3_4 => SGROUP_MERGE
781 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:B_3_4_X_3_4 vs. B:NULL => B:X_3_4 => SGROUP_MERGE
782
783 Test Replica vs. owned active: SGROUP vs. SGROUP tests
784 _SA_SA_DI_U<1c> => SGROUP_MERGE
785 _SA_SA_SI_U<1c> => SGROUP_MERGE
786 _SA_SA_SP_U<1c> => SGROUP_MERGE
787 _SA_SA_SB_U<1c> => SGROUP_MERGE
788 */
789 static NTSTATUS r_do_sgroup_merge(struct wreplsrv_partner *partner,
790                                   TALLOC_CTX *mem_ctx,
791                                   struct winsdb_record *rec,
792                                   struct wrepl_wins_owner *owner,
793                                   struct wrepl_name *replica)
794 {
795         struct winsdb_record *merge;
796         uint32_t modify_flags = 0;
797         uint32_t i,j;
798         uint8_t ret;
799         size_t len;
800         BOOL changed_old_addrs = False;
801         BOOL become_owner = True;
802
803         merge = talloc(mem_ctx, struct winsdb_record);
804         NT_STATUS_HAVE_NO_MEMORY(merge);
805
806         merge->name             = &replica->name;
807         merge->type             = replica->type;
808         merge->state            = replica->state;
809         merge->node             = replica->node;
810         merge->is_static        = replica->is_static;
811         merge->expire_time      = time(NULL) + partner->service->config.verify_interval;
812         merge->version          = replica->version_id;
813         merge->wins_owner       = replica->owner;
814         merge->addresses        = winsdb_addr_list_make(merge);
815         NT_STATUS_HAVE_NO_MEMORY(merge->addresses);
816         merge->registered_by = NULL;
817
818         len = winsdb_addr_list_length(rec->addresses);
819
820         for (i=0; i < len; i++) {
821                 BOOL found = False;
822
823                 for (j=0; j < replica->num_addresses; j++) {
824                         if (strcmp(rec->addresses[i]->address, replica->addresses[j].address) != 0) {
825                                 continue;
826                         }
827
828                         found = True;
829
830                         if (strcmp(rec->addresses[i]->wins_owner, replica->addresses[j].owner) != 0) {
831                                 changed_old_addrs = True;
832                                 break;
833                         }
834                         break;
835                 }
836
837                 /* if it's also in the replica, it'll added later */
838                 if (found) continue;
839
840                 /* 
841                  * if the address isn't in the replica and is owned by replicas owner,
842                  * it won't be added to the merged record
843                  */
844                 if (strcmp(rec->addresses[i]->wins_owner, owner->address) == 0) {
845                         changed_old_addrs = True;
846                         continue;
847                 }
848
849                 /*
850                  * add the address to the merge result, with the old owner and expire_time
851                  * TODO: check if that's correct for the expire_time
852                  */
853                 merge->addresses = winsdb_addr_list_add(merge->addresses,
854                                                         rec->addresses[i]->address,
855                                                         rec->addresses[i]->wins_owner,
856                                                         rec->addresses[i]->expire_time);
857                 NT_STATUS_HAVE_NO_MEMORY(merge->addresses);
858         }
859
860         for (i=0; i < replica->num_addresses; i++) {
861                 /* TODO: find out if rec->expire_time is correct here */
862                 merge->addresses = winsdb_addr_list_add(merge->addresses,
863                                                         replica->addresses[i].address,
864                                                         replica->addresses[i].owner,
865                                                         merge->expire_time);
866                 NT_STATUS_HAVE_NO_MEMORY(merge->addresses);
867         }
868
869         /* we the old addresses change changed we don't become the owner */
870         if (changed_old_addrs) {
871                 become_owner = False;
872         }
873
874         /* if we're the owner of the old record, we'll be the owner of the new one too */
875         if (strcmp(rec->wins_owner, WINSDB_OWNER_LOCAL)==0) {
876                 become_owner = True;
877         }
878
879         /*
880          * if the result has no addresses we take the ownership
881          */
882         len = winsdb_addr_list_length(merge->addresses);
883         if (len == 0) {
884                 become_owner = True;
885         }
886
887         /* 
888          * if addresses of the old record will be changed the replica owner
889          * will be owner of the merge result, otherwise we take the ownership
890          */
891         if (become_owner) {
892                 modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
893         }
894
895         ret = winsdb_modify(partner->service->wins_db, merge, modify_flags);
896         if (ret != NBT_RCODE_OK) {
897                 DEBUG(0,("Failed to modify sgroup merge record %s: %u\n",
898                         nbt_name_string(mem_ctx, &replica->name), ret));
899                 return NT_STATUS_FOOBAR;
900         }
901
902         DEBUG(4,("sgroup merge record %s\n",
903                 nbt_name_string(mem_ctx, &replica->name)));
904
905         return NT_STATUS_OK;
906 }
907
908 static NTSTATUS wreplsrv_apply_one_record(struct wreplsrv_partner *partner,
909                                           TALLOC_CTX *mem_ctx,
910                                           struct wrepl_wins_owner *owner,
911                                           struct wrepl_name *replica)
912 {
913         NTSTATUS status;
914         struct winsdb_record *rec = NULL;
915         enum _R_ACTION action = R_INVALID;
916         BOOL same_owner = False;
917         BOOL replica_vs_replica = False;
918         BOOL local_vs_replica = False;
919
920         status = winsdb_lookup(partner->service->wins_db,
921                                &replica->name, mem_ctx, &rec);
922         if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) {
923                 return r_do_add(partner, mem_ctx, owner, replica);
924         }
925         NT_STATUS_NOT_OK_RETURN(status);
926
927         if (strcmp(rec->wins_owner, WINSDB_OWNER_LOCAL)==0) {
928                 local_vs_replica = True;
929         } else if (strcmp(rec->wins_owner, owner->address)==0) {
930                 same_owner = True;
931         } else {
932                 replica_vs_replica = True;
933         }
934
935         if (rec->is_static && !same_owner) {
936                 /* TODO: this is just assumed and needs to be tested more */
937                 action = R_NOT_REPLACE;
938         } else if (same_owner) {
939                 action = replace_same_owner(rec, replica);
940         } else if (replica_vs_replica) {
941                 switch (rec->type) {
942                 case WREPL_TYPE_UNIQUE:
943                         action = replace_unique_replica_vs_X_replica(rec, replica);
944                         break;
945                 case WREPL_TYPE_GROUP:
946                         action = replace_group_replica_vs_X_replica(rec, replica);
947                         break;
948                 case WREPL_TYPE_SGROUP:
949                         action = replace_sgroup_replica_vs_X_replica(rec, replica);
950                         break;
951                 case WREPL_TYPE_MHOMED:
952                         action = replace_mhomed_replica_vs_X_replica(rec, replica);
953                         break;
954                 }
955         } else if (local_vs_replica) {
956                 switch (rec->type) {
957                 case WREPL_TYPE_UNIQUE:
958                         action = replace_unique_owned_vs_X_replica(rec, replica);
959                         break;
960                 case WREPL_TYPE_GROUP:
961                         action = replace_group_owned_vs_X_replica(rec, replica);
962                         break;
963                 case WREPL_TYPE_SGROUP:
964                         action = replace_sgroup_owned_vs_X_replica(rec, replica);
965                         break;
966                 case WREPL_TYPE_MHOMED:
967                         action = replace_mhomed_owned_vs_X_replica(rec, replica);
968                         break;
969                 }
970         }
971
972         DEBUG(4,("apply record %s: %s\n",
973                  nbt_name_string(mem_ctx, &replica->name), _R_ACTION_enum_string(action)));
974
975         switch (action) {
976         case R_INVALID: break;
977         case R_DO_REPLACE:
978                 return r_do_replace(partner, mem_ctx, rec, owner, replica);
979         case R_NOT_REPLACE:
980                 return r_not_replace(partner, mem_ctx, rec, owner, replica);
981         case R_DO_CHALLENGE:
982                 return r_do_challenge(partner, mem_ctx, rec, owner, replica);
983         case R_DO_RELEASE_DEMAND:
984                 return r_do_release_demand(partner, mem_ctx, rec, owner, replica);
985         case R_DO_SGROUP_MERGE: 
986                 return r_do_sgroup_merge(partner, mem_ctx, rec, owner, replica);
987         }
988
989         return NT_STATUS_INTERNAL_ERROR;
990 }
991
992 NTSTATUS wreplsrv_apply_records(struct wreplsrv_partner *partner, struct wreplsrv_pull_names_io *names_io)
993 {
994         NTSTATUS status;
995         uint32_t i;
996
997         DEBUG(4,("apply records count[%u]:owner[%s]:min[%llu]:max[%llu]:partner[%s]\n",
998                 names_io->out.num_names, names_io->in.owner.address,
999                 names_io->in.owner.min_version, names_io->in.owner.max_version,
1000                 partner->address));
1001
1002         for (i=0; i < names_io->out.num_names; i++) {
1003                 TALLOC_CTX *tmp_mem = talloc_new(partner);
1004                 NT_STATUS_HAVE_NO_MEMORY(tmp_mem);
1005
1006                 status = wreplsrv_apply_one_record(partner, tmp_mem,
1007                                                    &names_io->in.owner,
1008                                                    &names_io->out.names[i]);
1009                 talloc_free(tmp_mem);
1010                 NT_STATUS_NOT_OK_RETURN(status);
1011         }
1012
1013         status = wreplsrv_add_table(partner->service,
1014                                     partner->service,
1015                                     &partner->service->table,
1016                                     names_io->in.owner.address,
1017                                     names_io->in.owner.max_version);
1018         NT_STATUS_NOT_OK_RETURN(status);
1019
1020         return NT_STATUS_OK;
1021 }