root/usr/src/lib/libads/common/dsgetdc.c
/*
 * This file and its contents are supplied under the terms of the
 * Common Development and Distribution License ("CDDL"), version 1.0.
 * You may only use this file in accordance with the terms of version
 * 1.0 of the CDDL.
 *
 * A full copy of the text of the CDDL should have accompanied this
 * source.  A copy of the CDDL is also available via the Internet at
 * http://www.illumos.org/license/CDDL.
 */

/*
 * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
 * Copyright 2021 RackTop Systems, Inc.
 */

/*
 * MS-compatible Directory Server Discovery API, DsGetDC...()
 */

#include <stdlib.h>
#include <string.h>
#include <smb/nterror.h>
#include <smb/ntstatus.h>
#include <arpa/inet.h>
#include "dsgetdc.h"
#include "ads_priv.h"
#include <sys/debug.h>

#define DSGETDC_VALID_FLAGS ( \
        DS_FORCE_REDISCOVERY | \
        DS_DIRECTORY_SERVICE_REQUIRED | \
        DS_DIRECTORY_SERVICE_PREFERRED | \
        DS_GC_SERVER_REQUIRED | \
        DS_PDC_REQUIRED | \
        DS_BACKGROUND_ONLY | \
        DS_IP_REQUIRED | \
        DS_KDC_REQUIRED | \
        DS_TIMESERV_REQUIRED | \
        DS_WRITABLE_REQUIRED | \
        DS_GOOD_TIMESERV_PREFERRED | \
        DS_AVOID_SELF | \
        DS_ONLY_LDAP_NEEDED | \
        DS_IS_FLAT_NAME | \
        DS_IS_DNS_NAME | \
        DS_RETURN_FLAT_NAME | \
        DS_RETURN_DNS_NAME)

static struct timeval TIMEOUT = { 15, 0 };

/*
 * The Windows version of this would return a single allocation,
 * where any strings pointed to in the returned structure would be
 * stored in space following the top-level returned structure.
 * This allows NetApiBufferFree() to be the same as free().
 *
 * However, we don't have an easy way to do that right now, so
 * the dcinfo returned here will be free'd with DsFreeDcInfo().
 */
uint32_t
_DsGetDcName(const char *ComputerName,
    const char *DomainName, const struct uuid *DomainGuid,
    const char *SiteName, uint32_t Flags,
    DOMAIN_CONTROLLER_INFO **dcinfo)
{
        DsGetDcNameArgs args;
        DsGetDcNameRes res;
        CLIENT *clnt = NULL;
        enum clnt_stat clstat;

        *dcinfo = NULL;
        (void) memset(&args, 0, sizeof (args));
        (void) memset(&res, 0, sizeof (res));

        /*
         * Later check for over constrained optional args here,
         * and return (ERROR_INVALID_PARAMETER);
         */

        if (Flags & ~DSGETDC_VALID_FLAGS)
                return (ERROR_INVALID_FLAGS);

        /*
         * Call the ADS deamon.
         */
        clnt = clnt_door_create(ADSPRIV_PROGRAM, ADSPRIV_V1, ADSPRIV_MAX_XFER);
        if (clnt == NULL)
                return (RPC_S_NOT_LISTENING);

        args.ComputerName = (char *)ComputerName;
        args.DomainName = (char *)DomainName;
        if (DomainGuid != NULL)
                (void) memcpy(&args.DomainGuid, DomainGuid,
                    sizeof (args.DomainGuid));
        args.SiteName = (char *)SiteName;
        args.Flags = Flags;

        clstat = clnt_call(clnt, ADSPRIV_GetDcName,
            (xdrproc_t)xdr_DsGetDcNameArgs, (caddr_t)&args,
            (xdrproc_t)xdr_DsGetDcNameRes, (caddr_t)&res, TIMEOUT);

        clnt_destroy(clnt);
        if (clstat != RPC_SUCCESS)
                return (RPC_S_CALL_FAILED);
        if (res.status != 0)
                return (res.status);

        *dcinfo = malloc(sizeof (**dcinfo));
        if (*dcinfo == NULL)
                return (ERROR_NOT_ENOUGH_MEMORY);

        /*
         * We have taken pains to make these two the same.
         * DOMAIN_CONTROLLER_INFO / struct adspriv_dcinfo
         */
        CTASSERT(sizeof (**dcinfo) == sizeof (res.DsGetDcNameRes_u.res0));
        (void) memcpy(*dcinfo, &res.DsGetDcNameRes_u.res0, sizeof (**dcinfo));

        /*
         * NB: Do NOT xdr_free the result, because we're
         * returning a copy of it to the caller.
         */
        return (0);
}

int
DsGetDcName(const char *ComputerName,
    const char *DomainName, const struct uuid *DomainGuid,
    const char *SiteName, uint32_t Flags,
    DOMAIN_CONTROLLER_INFO **dcinfo)
{
        uint32_t status;
        int rc;

        status = _DsGetDcName(ComputerName, DomainName, DomainGuid,
            SiteName, Flags, dcinfo);

        switch (status) {
        case 0:
                rc = 0;
                break;
        case NT_STATUS_NO_SUCH_DOMAIN:  /* Specified domain unknown */
        case NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND:
        case NT_STATUS_CANT_WAIT:               /* or gave up waiting. */
        case NT_STATUS_INVALID_SERVER_STATE:    /*  not in domain mode. */
                rc = ERROR_NO_SUCH_DOMAIN;
                break;
        default:
                rc = ERROR_INTERNAL_ERROR;
                break;
        }
        return (rc);
}

void
DsFreeDcInfo(DOMAIN_CONTROLLER_INFO *dci)
{
        if (dci != NULL) {
                xdr_free(xdr_adspriv_dcinfo, (char *)dci);
                free(dci);
        }
}