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/util/dlinklist.h" 24#include "smbd/service_task.h" 25#include "smbd/service.h" 26#include "lib/messaging/irpc.h" 27#include "librpc/gen_ndr/winsrepl.h" 28#include "wrepl_server/wrepl_server.h" 29#include "nbt_server/wins/winsdb.h" 30#include "ldb/include/ldb.h" 31#include "ldb/include/ldb_errors.h" 32#include "auth/auth.h" 33#include "ldb_wrap.h" 34#include "param/param.h" 35#include "lib/socket/netif.h" 36 37static struct ldb_context *wins_config_db_connect(TALLOC_CTX *mem_ctx, 38 struct tevent_context *ev_ctx, 39 struct loadparm_context *lp_ctx) 40{ 41 return ldb_wrap_connect(mem_ctx, ev_ctx, lp_ctx, private_path(mem_ctx, 42 lp_ctx, lp_wins_config_url(lp_ctx)), 43 system_session(mem_ctx, lp_ctx), NULL, 0, NULL); 44} 45 46static uint64_t wins_config_db_get_seqnumber(struct ldb_context *ldb) 47{ 48 int ret; 49 struct ldb_dn *dn; 50 struct ldb_result *res = NULL; 51 TALLOC_CTX *tmp_ctx = talloc_new(ldb); 52 uint64_t seqnumber = 0; 53 54 dn = ldb_dn_new(tmp_ctx, ldb, "@BASEINFO"); 55 if (!dn) goto failed; 56 57 /* find the record in the WINS database */ 58 ret = ldb_search(ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, NULL, NULL); 59 if (ret != LDB_SUCCESS) goto failed; 60 if (res->count > 1) goto failed; 61 62 if (res->count == 1) { 63 seqnumber = ldb_msg_find_attr_as_uint64(res->msgs[0], "sequenceNumber", 0); 64 } 65 66failed: 67 talloc_free(tmp_ctx); 68 return seqnumber; 69} 70 71/* 72 open winsdb 73*/ 74static NTSTATUS wreplsrv_open_winsdb(struct wreplsrv_service *service, 75 struct loadparm_context *lp_ctx) 76{ 77 const char *owner = lp_parm_string(lp_ctx, NULL, "winsdb", "local_owner"); 78 79 if (owner == NULL) { 80 struct interface *ifaces; 81 load_interfaces(service, lp_interfaces(lp_ctx), &ifaces); 82 owner = iface_n_ip(ifaces, 0); 83 } 84 85 service->wins_db = winsdb_connect(service, service->task->event_ctx, lp_ctx, owner, WINSDB_HANDLE_CALLER_WREPL); 86 if (!service->wins_db) { 87 return NT_STATUS_INTERNAL_DB_ERROR; 88 } 89 90 service->config.ldb = wins_config_db_connect(service, service->task->event_ctx, lp_ctx); 91 if (!service->config.ldb) { 92 return NT_STATUS_INTERNAL_DB_ERROR; 93 } 94 95 /* the default renew interval is 6 days */ 96 service->config.renew_interval = lp_parm_int(lp_ctx, NULL,"wreplsrv","renew_interval", 6*24*60*60); 97 98 /* the default tombstone (extinction) interval is 6 days */ 99 service->config.tombstone_interval= lp_parm_int(lp_ctx, NULL,"wreplsrv","tombstone_interval", 6*24*60*60); 100 101 /* the default tombstone (extinction) timeout is 1 day */ 102 service->config.tombstone_timeout = lp_parm_int(lp_ctx, NULL,"wreplsrv","tombstone_timeout", 1*24*60*60); 103 104 /* the default tombstone extra timeout is 3 days */ 105 service->config.tombstone_extra_timeout = lp_parm_int(lp_ctx, NULL,"wreplsrv","tombstone_extra_timeout", 3*24*60*60); 106 107 /* the default verify interval is 24 days */ 108 service->config.verify_interval = lp_parm_int(lp_ctx, NULL,"wreplsrv","verify_interval", 24*24*60*60); 109 110 /* the default scavenging interval is 'renew_interval/2' */ 111 service->config.scavenging_interval=lp_parm_int(lp_ctx, NULL,"wreplsrv","scavenging_interval", 112 service->config.renew_interval/2); 113 114 /* the maximun interval to the next periodic processing event */ 115 service->config.periodic_interval = lp_parm_int(lp_ctx, NULL,"wreplsrv","periodic_interval", 15); 116 117 return NT_STATUS_OK; 118} 119 120struct wreplsrv_partner *wreplsrv_find_partner(struct wreplsrv_service *service, const char *peer_addr) 121{ 122 struct wreplsrv_partner *cur; 123 124 for (cur = service->partners; cur; cur = cur->next) { 125 if (strcmp(cur->address, peer_addr) == 0) { 126 return cur; 127 } 128 } 129 130 return NULL; 131} 132 133/* 134 load our replication partners 135*/ 136NTSTATUS wreplsrv_load_partners(struct wreplsrv_service *service) 137{ 138 struct wreplsrv_partner *partner; 139 struct ldb_result *res = NULL; 140 int ret; 141 TALLOC_CTX *tmp_ctx; 142 int i; 143 uint64_t new_seqnumber; 144 145 new_seqnumber = wins_config_db_get_seqnumber(service->config.ldb); 146 147 /* if it's not the first run and nothing changed we're done */ 148 if (service->config.seqnumber != 0 && service->config.seqnumber == new_seqnumber) { 149 return NT_STATUS_OK; 150 } 151 152 tmp_ctx = talloc_new(service); 153 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx); 154 155 service->config.seqnumber = new_seqnumber; 156 157 /* find the record in the WINS database */ 158 ret = ldb_search(service->config.ldb, tmp_ctx, &res, 159 ldb_dn_new(tmp_ctx, service->config.ldb, "CN=PARTNERS"), 160 LDB_SCOPE_SUBTREE, NULL, "(objectClass=wreplPartner)"); 161 if (ret != LDB_SUCCESS) goto failed; 162 163 /* first disable all existing partners */ 164 for (partner=service->partners; partner; partner = partner->next) { 165 partner->type = WINSREPL_PARTNER_NONE; 166 } 167 168 for (i=0; i < res->count; i++) { 169 const char *address; 170 171 address = ldb_msg_find_attr_as_string(res->msgs[i], "address", NULL); 172 if (!address) { 173 goto failed; 174 } 175 176 partner = wreplsrv_find_partner(service, address); 177 if (partner) { 178 if (partner->name != partner->address) { 179 talloc_free(discard_const(partner->name)); 180 } 181 partner->name = NULL; 182 talloc_free(discard_const(partner->our_address)); 183 partner->our_address = NULL; 184 185 /* force rescheduling of pulling */ 186 partner->pull.next_run = timeval_zero(); 187 } else { 188 partner = talloc_zero(service, struct wreplsrv_partner); 189 if (partner == NULL) goto failed; 190 191 partner->service = service; 192 partner->address = address; 193 talloc_steal(partner, partner->address); 194 195 DLIST_ADD_END(service->partners, partner, struct wreplsrv_partner *); 196 } 197 198 partner->name = ldb_msg_find_attr_as_string(res->msgs[i], "name", partner->address); 199 talloc_steal(partner, partner->name); 200 partner->our_address = ldb_msg_find_attr_as_string(res->msgs[i], "ourAddress", NULL); 201 talloc_steal(partner, partner->our_address); 202 203 partner->type = ldb_msg_find_attr_as_uint(res->msgs[i], "type", WINSREPL_PARTNER_BOTH); 204 partner->pull.interval = ldb_msg_find_attr_as_uint(res->msgs[i], "pullInterval", 205 WINSREPL_DEFAULT_PULL_INTERVAL); 206 partner->pull.retry_interval = ldb_msg_find_attr_as_uint(res->msgs[i], "pullRetryInterval", 207 WINSREPL_DEFAULT_PULL_RETRY_INTERVAL); 208 partner->push.change_count = ldb_msg_find_attr_as_uint(res->msgs[i], "pushChangeCount", 209 WINSREPL_DEFAULT_PUSH_CHANGE_COUNT); 210 partner->push.use_inform = ldb_msg_find_attr_as_uint(res->msgs[i], "pushUseInform", true); 211 212 DEBUG(3,("wreplsrv_load_partners: found partner: %s type: 0x%X\n", 213 partner->address, partner->type)); 214 } 215 216 DEBUG(2,("wreplsrv_load_partners: %u partners found: wins_config_db seqnumber %llu\n", 217 res->count, (unsigned long long)service->config.seqnumber)); 218 219 talloc_free(tmp_ctx); 220 return NT_STATUS_OK; 221failed: 222 talloc_free(tmp_ctx); 223 return NT_STATUS_FOOBAR; 224} 225 226NTSTATUS wreplsrv_fill_wrepl_table(struct wreplsrv_service *service, 227 TALLOC_CTX *mem_ctx, 228 struct wrepl_table *table_out, 229 const char *initiator, 230 bool full_table) 231{ 232 struct wreplsrv_owner *cur; 233 uint32_t i = 0; 234 235 table_out->partner_count = 0; 236 table_out->partners = NULL; 237 table_out->initiator = initiator; 238 239 for (cur = service->table; cur; cur = cur->next) { 240 if (full_table) { 241 table_out->partner_count++; 242 continue; 243 } 244 245 if (strcmp(initiator, cur->owner.address) != 0) continue; 246 247 table_out->partner_count++; 248 break; 249 } 250 251 table_out->partners = talloc_array(mem_ctx, struct wrepl_wins_owner, table_out->partner_count); 252 NT_STATUS_HAVE_NO_MEMORY(table_out->partners); 253 254 for (cur = service->table; cur && i < table_out->partner_count; cur = cur->next) { 255 /* 256 * if it's our local entry 257 * update the max version 258 */ 259 if (cur == service->owner) { 260 cur->owner.max_version = winsdb_get_maxVersion(service->wins_db); 261 } 262 263 if (full_table) { 264 table_out->partners[i] = cur->owner; 265 i++; 266 continue; 267 } 268 269 if (strcmp(initiator, cur->owner.address) != 0) continue; 270 271 table_out->partners[i] = cur->owner; 272 i++; 273 break; 274 } 275 276 return NT_STATUS_OK; 277} 278 279struct wreplsrv_owner *wreplsrv_find_owner(struct wreplsrv_service *service, 280 struct wreplsrv_owner *table, 281 const char *wins_owner) 282{ 283 struct wreplsrv_owner *cur; 284 285 for (cur = table; cur; cur = cur->next) { 286 if (strcmp(cur->owner.address, wins_owner) == 0) { 287 /* 288 * if it's our local entry 289 * update the max version 290 */ 291 if (cur == service->owner) { 292 cur->owner.max_version = winsdb_get_maxVersion(service->wins_db); 293 } 294 return cur; 295 } 296 } 297 298 return NULL; 299} 300 301/* 302 update the wins_owner_table max_version, if the given version is the highest version 303 if no entry for the wins_owner exists yet, create one 304*/ 305NTSTATUS wreplsrv_add_table(struct wreplsrv_service *service, 306 TALLOC_CTX *mem_ctx, struct wreplsrv_owner **_table, 307 const char *wins_owner, uint64_t version) 308{ 309 struct wreplsrv_owner *table = *_table; 310 struct wreplsrv_owner *cur; 311 312 if (!wins_owner || strcmp(wins_owner, "0.0.0.0") == 0) { 313 wins_owner = service->wins_db->local_owner; 314 } 315 316 cur = wreplsrv_find_owner(service, table, wins_owner); 317 318 /* if it doesn't exists yet, create one */ 319 if (!cur) { 320 cur = talloc_zero(mem_ctx, struct wreplsrv_owner); 321 NT_STATUS_HAVE_NO_MEMORY(cur); 322 323 cur->owner.address = talloc_strdup(cur, wins_owner); 324 NT_STATUS_HAVE_NO_MEMORY(cur->owner.address); 325 cur->owner.min_version = 0; 326 cur->owner.max_version = 0; 327 cur->owner.type = 1; /* don't know why this is always 1 */ 328 329 cur->partner = wreplsrv_find_partner(service, wins_owner); 330 331 DLIST_ADD_END(table, cur, struct wreplsrv_owner *); 332 *_table = table; 333 } 334 335 /* the min_version is always 0 here, and won't be updated */ 336 337 /* if the given version is higher than the current max_version, update */ 338 if (cur->owner.max_version < version) { 339 cur->owner.max_version = version; 340 /* if it's for our local db, we need to update the wins.ldb too */ 341 if (cur == service->owner) { 342 uint64_t ret; 343 ret = winsdb_set_maxVersion(service->wins_db, cur->owner.max_version); 344 if (ret != cur->owner.max_version) { 345 DEBUG(0,("winsdb_set_maxVersion(%llu) failed: %llu\n", 346 (unsigned long long)cur->owner.max_version, 347 (unsigned long long)ret)); 348 return NT_STATUS_INTERNAL_DB_CORRUPTION; 349 } 350 } 351 } 352 353 return NT_STATUS_OK; 354} 355 356/* 357 load the partner table 358*/ 359static NTSTATUS wreplsrv_load_table(struct wreplsrv_service *service) 360{ 361 struct ldb_result *res = NULL; 362 int ret; 363 NTSTATUS status; 364 TALLOC_CTX *tmp_ctx = talloc_new(service); 365 struct ldb_context *ldb = service->wins_db->ldb; 366 int i; 367 struct wreplsrv_owner *local_owner; 368 const char *wins_owner; 369 uint64_t version; 370 const char * const attrs[] = { 371 "winsOwner", 372 "versionID", 373 NULL 374 }; 375 376 /* 377 * make sure we have our local entry in the list, 378 * but we set service->owner when we're done 379 * to avoid to many calls to wreplsrv_local_max_version() 380 */ 381 status = wreplsrv_add_table(service, 382 service, &service->table, 383 service->wins_db->local_owner, 0); 384 if (!NT_STATUS_IS_OK(status)) goto failed; 385 local_owner = wreplsrv_find_owner(service, service->table, service->wins_db->local_owner); 386 if (!local_owner) { 387 status = NT_STATUS_INTERNAL_ERROR; 388 goto failed; 389 } 390 391 /* find the record in the WINS database */ 392 ret = ldb_search(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, 393 attrs, "(objectClass=winsRecord)"); 394 status = NT_STATUS_INTERNAL_DB_CORRUPTION; 395 if (ret != LDB_SUCCESS) goto failed; 396 397 for (i=0; i < res->count; i++) { 398 wins_owner = ldb_msg_find_attr_as_string(res->msgs[i], "winsOwner", NULL); 399 version = ldb_msg_find_attr_as_uint64(res->msgs[i], "versionID", 0); 400 401 status = wreplsrv_add_table(service, 402 service, &service->table, 403 wins_owner, version); 404 if (!NT_STATUS_IS_OK(status)) goto failed; 405 talloc_free(res->msgs[i]); 406 } 407 408 /* 409 * this makes sure we call wreplsrv_local_max_version() before returning in 410 * wreplsrv_find_owner() 411 */ 412 service->owner = local_owner; 413 414 /* 415 * this makes sure the maxVersion in the database is updated, 416 * with the highest version we found, if this is higher than the current stored one 417 */ 418 status = wreplsrv_add_table(service, 419 service, &service->table, 420 service->wins_db->local_owner, local_owner->owner.max_version); 421 if (!NT_STATUS_IS_OK(status)) goto failed; 422 423 talloc_free(tmp_ctx); 424 return NT_STATUS_OK; 425failed: 426 talloc_free(tmp_ctx); 427 return status; 428} 429 430/* 431 setup our replication partners 432*/ 433static NTSTATUS wreplsrv_setup_partners(struct wreplsrv_service *service) 434{ 435 NTSTATUS status; 436 437 status = wreplsrv_load_partners(service); 438 NT_STATUS_NOT_OK_RETURN(status); 439 440 status = wreplsrv_load_table(service); 441 NT_STATUS_NOT_OK_RETURN(status); 442 443 return NT_STATUS_OK; 444} 445 446/* 447 startup the wrepl task 448*/ 449static void wreplsrv_task_init(struct task_server *task) 450{ 451 NTSTATUS status; 452 struct wreplsrv_service *service; 453 454 if (!lp_wins_support(task->lp_ctx)) { 455 return; 456 } 457 458 task_server_set_title(task, "task[wreplsrv]"); 459 460 service = talloc_zero(task, struct wreplsrv_service); 461 if (!service) { 462 task_server_terminate(task, "wreplsrv_task_init: out of memory", true); 463 return; 464 } 465 service->task = task; 466 service->startup_time = timeval_current(); 467 task->private_data = service; 468 469 /* 470 * setup up all partners, and open the winsdb 471 */ 472 status = wreplsrv_open_winsdb(service, task->lp_ctx); 473 if (!NT_STATUS_IS_OK(status)) { 474 task_server_terminate(task, "wreplsrv_task_init: wreplsrv_open_winsdb() failed", true); 475 return; 476 } 477 478 /* 479 * setup timed events for each partner we want to pull from 480 */ 481 status = wreplsrv_setup_partners(service); 482 if (!NT_STATUS_IS_OK(status)) { 483 task_server_terminate(task, "wreplsrv_task_init: wreplsrv_setup_partners() failed", true); 484 return; 485 } 486 487 /* 488 * setup listen sockets, so we can anwser requests from our partners, 489 * which pull from us 490 */ 491 status = wreplsrv_setup_sockets(service, task->lp_ctx); 492 if (!NT_STATUS_IS_OK(status)) { 493 task_server_terminate(task, "wreplsrv_task_init: wreplsrv_setup_sockets() failed", true); 494 return; 495 } 496 497 status = wreplsrv_setup_periodic(service); 498 if (!NT_STATUS_IS_OK(status)) { 499 task_server_terminate(task, "wreplsrv_task_init: wreplsrv_setup_periodic() failed", true); 500 return; 501 } 502 503 irpc_add_name(task->msg_ctx, "wrepl_server"); 504} 505 506/* 507 register ourselves as a available server 508*/ 509NTSTATUS server_service_wrepl_init(void) 510{ 511 return register_server_service("wrepl", wreplsrv_task_init); 512} 513