NTSTATUS winsdb_record(struct winsdb_handle *h, struct ldb_message *msg, TALLOC_CTX *mem_ctx, time_t now, struct winsdb_record **_rec)
{
	NTSTATUS status;
	struct winsdb_record *rec;
	struct ldb_message_element *el;
	struct nbt_name *name;
	uint32_t i, j, num_values;

	rec = talloc(mem_ctx, struct winsdb_record);
	if (rec == NULL) {
		status = NT_STATUS_NO_MEMORY;
		goto failed;
	}

	status = winsdb_nbt_name(rec, msg->dn, &name);
	if (!NT_STATUS_IS_OK(status)) goto failed;

	if (strlen(name->name) > 15) {
		status = NT_STATUS_INTERNAL_DB_CORRUPTION;
		goto failed;
	}
	if (name->scope && strlen(name->scope) > 238) {
		status = NT_STATUS_INTERNAL_DB_CORRUPTION;
		goto failed;
	}

	/* parse it into a more convenient winsdb_record structure */
	rec->name		= name;
	rec->type		= ldb_msg_find_attr_as_int(msg, "recordType", WREPL_TYPE_UNIQUE);
	rec->state		= ldb_msg_find_attr_as_int(msg, "recordState", WREPL_STATE_RELEASED);
	rec->node		= ldb_msg_find_attr_as_int(msg, "nodeType", WREPL_NODE_B);
	rec->is_static		= ldb_msg_find_attr_as_int(msg, "isStatic", 0);
	rec->expire_time	= ldb_string_to_time(ldb_msg_find_attr_as_string(msg, "expireTime", NULL));
	rec->version		= ldb_msg_find_attr_as_uint64(msg, "versionID", 0);
	rec->wins_owner		= ldb_msg_find_attr_as_string(msg, "winsOwner", NULL);
	rec->registered_by	= ldb_msg_find_attr_as_string(msg, "registeredBy", NULL);
	talloc_steal(rec, rec->wins_owner);
	talloc_steal(rec, rec->registered_by);

	if (!rec->wins_owner || strcmp(rec->wins_owner, "0.0.0.0") == 0) {
		rec->wins_owner = h->local_owner;
	}

	el = ldb_msg_find_element(msg, "address");
	if (el) {
		num_values = el->num_values;
	} else {
		num_values = 0;
	}

	if (rec->type == WREPL_TYPE_UNIQUE || rec->type == WREPL_TYPE_GROUP) {
		if (num_values != 1) {
			status = NT_STATUS_INTERNAL_DB_CORRUPTION;
			goto failed;
		}
	}
	if (rec->state == WREPL_STATE_ACTIVE) {
		if (num_values < 1) {
			status = NT_STATUS_INTERNAL_DB_CORRUPTION;
			goto failed;
		}
	}
	if (num_values > 25) {
		status = NT_STATUS_INTERNAL_DB_CORRUPTION;
		goto failed;
	}

	rec->addresses     = talloc_array(rec, struct winsdb_addr *, num_values+1);
	if (rec->addresses == NULL) {
		status = NT_STATUS_NO_MEMORY;
		goto failed;
	}

	for (i=0,j=0;i<num_values;i++) {
		bool we_are_owner = false;

		status = winsdb_addr_decode(h, rec, &el->values[i], rec->addresses, &rec->addresses[j]);
		if (!NT_STATUS_IS_OK(status)) goto failed;

		if (strcmp(rec->addresses[j]->wins_owner, h->local_owner) == 0) {
			we_are_owner = true;
		}

		/*
		 * the record isn't static and is active
		 * then don't add the address if it's expired,
		 * but only if we're the owner of the address
		 *
		 * This is important for SGROUP records,
		 * because each server thinks he's the owner of the
		 * record and the record isn't replicated on a
		 * name_refresh. So addresses owned by another owner
		 * could expire, but we still need to return them
		 * (as windows does).
		 */
		if (!rec->is_static &&
		    rec->addresses[j]->expire_time <= now &&
		    rec->state == WREPL_STATE_ACTIVE &&
		    we_are_owner) {
			DEBUG(5,("WINS: expiring name addr %s of %s (expired at %s)\n", 
				 rec->addresses[j]->address, nbt_name_string(rec->addresses[j], rec->name),
				 timestring(rec->addresses[j], rec->addresses[j]->expire_time)));
			talloc_free(rec->addresses[j]);
			rec->addresses[j] = NULL;
			continue;
		}
		j++;
	}
	rec->addresses[j] = NULL;
	num_values = j;

	if (rec->is_static && rec->state == WREPL_STATE_ACTIVE) {
		rec->expire_time = get_time_t_max();
		for (i=0;rec->addresses[i];i++) {
			rec->addresses[i]->expire_time = rec->expire_time;
		}
	}

	if (rec->state == WREPL_STATE_ACTIVE) {
		if (num_values < 1) {
			DEBUG(5,("WINS: expiring name %s (because it has no active addresses)\n", 
				 nbt_name_string(mem_ctx, rec->name)));
			rec->state = WREPL_STATE_RELEASED;
		}
	}

	*_rec = rec;
	return NT_STATUS_OK;
failed:
	if (NT_STATUS_EQUAL(NT_STATUS_INTERNAL_DB_CORRUPTION, status)) {
		DEBUG(1,("winsdb_record: corrupted record: %s\n", ldb_dn_get_linearized(msg->dn)));
	}
	talloc_free(rec);
	return status;
}