static int find_spn_aliases(struct ldb_context *ldb,
			    TALLOC_CTX *mem_ctx,
			    const char *service_class,
			    char ***aliases,
			    size_t *n_aliases,
			    int *direction)
{
	/*
	 * If you change the way this works, you should also look at changing
	 * LDB_lookup_spn_alias() in source4/dsdb/samdb/cracknames.c, which
	 * does some of the same work.
	 *
	 * In particular, note that sPNMappings are resolved on a first come,
	 * first served basis. For example, if we have
	 *
	 *  host=ldap,cifs
	 *  foo=ldap
	 *  cifs=host,alerter
	 *
	 * then 'ldap', 'cifs', and 'host' will resolve to 'host', and
	 * 'alerter' will resolve to 'cifs'.
	 *
	 * If this resolution method is made more complicated, then the
	 * cracknames function should also be changed.
	 */
	size_t i, j;
	int ret;
	bool ok;
	struct ldb_result *res = NULL;
	struct ldb_message_element *spnmappings = NULL;
	TALLOC_CTX *tmp_ctx = NULL;
	struct ldb_dn *service_dn = NULL;

	const char *attrs[] = {
		"sPNMappings",
		NULL
	};

	*direction = SPN_ALIAS_NONE;

	tmp_ctx = talloc_new(mem_ctx);
	if (tmp_ctx == NULL) {
		return ldb_oom(ldb);
	}

	service_dn = ldb_dn_new(
		tmp_ctx, ldb,
		"CN=Directory Service,CN=Windows NT,CN=Services");
	if (service_dn == NULL) {
		talloc_free(tmp_ctx);
		return ldb_oom(ldb);
	}

	ok = ldb_dn_add_base(service_dn, ldb_get_config_basedn(ldb));
	if (! ok) {
		talloc_free(tmp_ctx);
		return LDB_ERR_OPERATIONS_ERROR;
	}

	ret = ldb_search(ldb, tmp_ctx, &res, service_dn, LDB_SCOPE_BASE,
			 attrs, "(objectClass=nTDSService)");

	if (ret != LDB_SUCCESS || res->count != 1) {
		DBG_WARNING("sPNMappings not found.\n");
		talloc_free(tmp_ctx);
		return ret;
	}

	spnmappings = ldb_msg_find_element(res->msgs[0], "sPNMappings");
	if (spnmappings == NULL || spnmappings->num_values == 0) {
		DBG_WARNING("no sPNMappings attribute\n");
		talloc_free(tmp_ctx);
		return LDB_ERR_NO_SUCH_OBJECT;
	}
	*n_aliases = 0;

	for (i = 0; i < spnmappings->num_values; i++) {
		char *p = NULL;
		char *mapping = talloc_strndup(
			tmp_ctx,
			(char *)spnmappings->values[i].data,
			spnmappings->values[i].length);
		if (mapping == NULL) {
			talloc_free(tmp_ctx);
			return ldb_oom(ldb);
		}

		p = strchr(mapping, '=');
		if (p == NULL) {
			talloc_free(tmp_ctx);
			return LDB_ERR_ALIAS_PROBLEM;
		}
		p[0] = '\0';
		p++;

		if (strcasecmp(mapping, service_class) == 0) {
			/*
			 * We need to return the reverse aliases for this one.
			 *
			 * typically, this means the service_class is "host"
			 * and the mapping is "host=alerter,appmgmt,cisvc,..",
			 * so we get "alerter", "appmgmt", etc in the list of
			 * aliases.
			 */

			/* There is one more field than there are commas */
			size_t n = 1;

			for (j = 0; p[j] != '\0'; j++) {
				if (p[j] == ',') {
					n++;
					p[j] = '\0';
				}
			}
			*aliases = talloc_array(mem_ctx, char*, n);
			if (*aliases == NULL) {
				talloc_free(tmp_ctx);
				return ldb_oom(ldb);
			}
			*n_aliases = n;
			talloc_steal(mem_ctx, mapping);
			for (j = 0; j < n; j++) {
				(*aliases)[j] = p;
				p += strlen(p) + 1;
			}
			talloc_free(tmp_ctx);
			*direction = SPN_ALIAS_LINK;
			return LDB_SUCCESS;
		}
		/*
		 * We need to look along the list to see if service_class is
		 * there; if so, we return a list of one item (probably "host").
		 */
		do {
			char *str = p;
			p = strchr(p, ',');
			if (p != NULL) {
				p[0] = '\0';
				p++;
			}
			if (strcasecmp(str, service_class) == 0) {
				*aliases = talloc_array(mem_ctx, char*, 1);
				if (*aliases == NULL) {
					talloc_free(tmp_ctx);
					return ldb_oom(ldb);
				}
				*n_aliases = 1;
				(*aliases)[0] = mapping;
				talloc_steal(mem_ctx, mapping);
				talloc_free(tmp_ctx);
				*direction = SPN_ALIAS_TARGET;
				return LDB_SUCCESS;
			}
		} while (p != NULL);
	}
	DBG_INFO("no sPNMappings alias for '%s'\n", service_class);
	talloc_free(tmp_ctx);
	*aliases = NULL;
	*n_aliases = 0;
	return LDB_SUCCESS;
}