// BUGBUG: NameSize and TreeConnName are different!!

/*++

Copyright (c) 1987-92  Microsoft Corporation

Module Name:

    Transact.c

Abstract:

    RxpTransactSmb (analogous to the LanMan 2.x transact routine) performs a
    transaction FSCTL to the redirector.

Author:

    John Rogers (JohnRo) 01-Apr-1991  (NT version only)

Environment:

    Only runs under NT, although the interface is portable (Win/32).
    Requires ANSI C extensions: slash-slash comments, long external names.

Revision History:

    various
        Original code (from LanMan 2.x).
    01-Apr-91 JohnRo
        Converted code from LanMan (OS/2) to NT.
    02-Apr-1991 JohnRo
        Moved NetpRdrFsControlTree to <netlibnt.h>.
    17-Apr-1991 JohnRo
        Fixed memory leaks (especially with pipe name).
        Quiet debug output by default.
        Reduced recompile hits from header files.
    03-May-1991 JohnRo
        Pass UNC server name for ease of use.  Use Unicode transitional types.
        Use UNREFERENCED_PARAMETER() macro.
    15-May-1991 JohnRo
        Use FORMAT_LPVOID instead of FORMAT_POINTER, for maximum portability.
    22-May-1991 JohnRo
        Use correct string handling functions to allow UNICODE.
        Use NetpDbgReasonable().
    14-Jul-1991 JohnRo
        Don't do assert on server name.
    17-Jul-1991 JohnRo
        Extracted RxpDebug.h from Rxp.h.
    04-Oct-1991 JohnRo
        Generate NERR_BadTransactConfig when IPC$ isn't shared.
        Clarified a debug output message.  Use TEXT() macro.
    01-Nov-1991 JohnRo
        Don't let the new (paranoid) RxpFatalErrorCode() prevent debug output.
    16-Jan-1992 JohnRo
        The redirector always expects UNICODE for the transact parm name.
    31-Mar-1992 JohnRo
        Prevent too large size requests.
    22-Sep-1992 JohnRo
        RAID 6739: Browser too slow when not logged into browsed domain.

--*/

// These must be included first:

#include <nt.h>                 // Needed by netlibnt.h.
#include <rxp.h>                // RpcXlate's private header file.

// These may be included in any order:

#include <apiworke.h>           // REM_APITXT, APIEXTR.
#include <lmerr.h>              // NERR_ and ERROR_ equates.
#include <names.h>              // NetpIsComputerNameValid().
#include <netdebug.h>   // NetpAssert(), NetpKdPrint(()), FORMAT_ equates.
#include <netlib.h>             // NetpMoveMemory(), etc.
#include <ntddnfs.h>            // TRANSACTION_REQUEST, etc.
#include <prefix.h>     // PREFIX_ equates.
#include <rxpdebug.h>           // IF_DEBUG().
#include <tstring.h>            // STRCAT(), STRCPY(), STRLEN().
#include <lmuse.h>

#ifdef CDEBUG
#include <apinums.h>            // API_WServerGetInfo, etc.
#include <netlib.h>             // NetpPackString().
#include <smbgtpt.h>            // SmbGetUshort().
#include <server.h>             // SERVER_INFO_100.
#endif // CDEBUG

#include <netlibnt.h>           // NetpRdrFsControlTree().


NET_API_STATUS
RxpTransactSmb(
    IN LPTSTR UncServerName,
    IN LPTSTR TransportName OPTIONAL,
    IN LPVOID SendParmPtr,
    IN DWORD SendParmLen,
    IN LPVOID SendDataPtr OPTIONAL,
    IN DWORD SendDataLen,
    OUT LPVOID RetParmPtr OPTIONAL,
    IN DWORD RetParmLen,
    OUT LPVOID RetDataPtr OPTIONAL,
    IN OUT LPDWORD RetDataLen,
    IN BOOL NoPermissionRequired
    )

/*++

Routine Description:

    RxpTransactSmb takes the caller's parameters and builds a transaction
    SMB which is sent to a remote machine.  This routine waits for the
    response to this SMB and returns the status from it.

Arguments:

    UncServerName - Server name to transact with (including \\).

    SendParmPtr - Pointer to send parameters.

    SendParmLen - Length of send parameters.

    SendDataPtr - Optional pointer to send data.

    SendDataLen - Send data length.

    RetParmPtr - Optional pointer to buffer for return parameters.

    RetParmLen - Expected length of return parameters.

    RetDataPtr - Optional pointer to buffer for return data.

    RetDataLen - IN: Expected length of return data.
                 OUT: Received length of return data.

    NoPermissionRequired - TRUE if this is a no permission required API.  (I.e.
        TRUE if the null session may be used.)

Return Value:

    (various values as returned by the remote API, plus values which can
    be generated by this routine)

--*/

/*
 * Note 1: how the packet is build and sized.
 *
 *      The paramater buffer for the transaction consists of the
 *      transaction parameter structure, followed by the name of the
 *      target pipe and the password, which is always NULL.  The pipe
 *      name and password are ASCIZ strings.
 *
 *      We build the pipe by taking the canonicalized server name, and
 *      appending the text REM_APITXT (see net/inc/apiworke.h).  This text
 *      contains the pipe suffix (\pipe\lanman) plus TWO nulls, one to
 *      terminate the pipe name and one to terminate the (empty) password.
 *
 *      So, the maximum buffer size is as shown below for the allocation
 *      of ioctl_buf.  UNCLEN is the max len of the canonicalized
 *      UncServerName and includes the two leading slashes, but not any
 *      terminating NUL.  The terminating NUL, as well as the pipe suffix
 *      and the emptry password, are accounted for in APIEXTR.
 *
 *      Our actual size is the same, except substitute the length of the
 *      canonicalized UncServerName for UNCLEN.  This is how ParmRktLen is
 *      calculated.
 *
 */

{
#ifndef CDEBUG
    PLMR_TRANSACTION FsctlParms;        // Parms to tell redir what to do.
    DWORD FsctlParmSize;                // Size of FsctlParms and strings.
    LPTSTR TreeConnName;                // LM-style server & share name.
#endif // ndef CDEBUG
    NET_API_STATUS Status;

//
// MOD 06/11/91 RLF
// Create DWORD variable to avoid indirection every time RetDataLen accessed
//
    DWORD   InputRetDataLen = *RetDataLen;
//
// MOD 06/11/91 RLF
//

    IF_DEBUG(TRANSACT) {
        NetpKdPrint(( PREFIX_NETAPI
                "RxpTransactSmb: entered, servername='"
                FORMAT_LPTSTR "'...\n", UncServerName));
        NetpKdPrint(( PREFIX_NETAPI
                "RxpTransactSmb: SendParm at " FORMAT_LPVOID
                ", len=" FORMAT_DWORD " (partial):\n",
                (LPVOID) SendParmPtr, SendParmLen));
        if (SendParmPtr != NULL) {
            NetpDbgHexDump(SendParmPtr, NetpDbgReasonable(SendParmLen));
        }
        NetpKdPrint(( PREFIX_NETAPI
                "RxpTransactSmb: SendData at " FORMAT_LPVOID
                ", len=" FORMAT_DWORD " (partial):\n",
                (LPVOID) SendDataPtr, SendDataLen));
        if (SendDataPtr != NULL) {
            NetpDbgHexDump(SendDataPtr, NetpDbgReasonable(SendDataLen));
        }
        NetpKdPrint(( PREFIX_NETAPI
                "RxpTransactSmb: RetParmPtr at " FORMAT_LPVOID
                ", len=" FORMAT_DWORD ".\n", (LPVOID) RetParmPtr, RetParmLen));

        NetpKdPrint(( PREFIX_NETAPI
                "RxpTransactSmb: (old) RetData at " FORMAT_LPVOID ", "
                "len=" FORMAT_DWORD " (partial):\n",
                (LPVOID) RetDataPtr, InputRetDataLen));
        if (RetDataPtr != NULL) {
            NetpDbgHexDump(RetDataPtr, NetpDbgReasonable(InputRetDataLen));
        }
    }

    NetpAssert( SendParmLen     <= MAX_TRANSACT_SEND_PARM_SIZE );
    NetpAssert( SendDataLen     <= MAX_TRANSACT_SEND_DATA_SIZE );
    NetpAssert( RetParmLen      <= MAX_TRANSACT_RET_PARM_SIZE );
    NetpAssert( InputRetDataLen <= MAX_TRANSACT_RET_DATA_SIZE );

    // Assumes that isremote(UncServerName) has already checked for
    // a NULL and empty string.

    if ((UncServerName == NULL) || (UncServerName[0] == 0)) {
        NetpBreakPoint();
        return (NERR_InternalError);
    }

    if (! NetpIsUncComputerNameValid(UncServerName)) {
        return (NERR_InvalidComputer);
    }

    IF_DEBUG(TRANSACT) {
        NetpKdPrint(( PREFIX_NETAPI
                "RxpTransactSmb: pipe name is '" FORMAT_LPWSTR
                "'.\n", REM_APITXT));
    }

#ifndef CDEBUG
    //
    // Build NT-style name for what we're connecting to.  Note that there is
    // NOT a pair of backslashes anywhere in this name.
    //

    {
        DWORD NameSize =

            // /Device/LanManRedirector    /server                  /IPC$\0
            ( strlen(DD_NFS_DEVICE_NAME) + STRLEN(UncServerName)-1 + 6 )
            * sizeof(TCHAR);

        TreeConnName = NetpMemoryAllocate( NameSize );
    }

    if (TreeConnName == NULL) {
        return (ERROR_NOT_ENOUGH_MEMORY);
    }

    //
    // Build the tree connect name.
    //

    (void) STRCPY(TreeConnName, UncServerName);           // copy "\\server",
    (void) STRCAT(TreeConnName, (LPTSTR) TEXT("\\IPC$")); // then "\share".
    IF_DEBUG(TRANSACT) {
        NetpKdPrint(( PREFIX_NETAPI
                "RxpTransactSmb: TreeConnName is '" FORMAT_LPTSTR
                "'.\n", TreeConnName));
    }

    // BUGBUG: Should we check tree connect name here?   We shouldn't
    // have an assertion on it, as nobody else was responsible for checking.
    // NetpAssert(NetpIsRemoteNameValid(TreeConnName));

    // Set FsctlParmSize and allocate fsctl structure.
    FsctlParmSize = sizeof(LMR_TRANSACTION) + (APIEXTR);
    FsctlParms = NetpMemoryAllocate(FsctlParmSize);
    if (FsctlParms == NULL) {
        NetpMemoryFree(TreeConnName);
        return (ERROR_NOT_ENOUGH_MEMORY);
    }
    IF_DEBUG(TRANSACT) {
        NetpKdPrint(( PREFIX_NETAPI
                "RxpTransactSmb: allocated " FORMAT_DWORD
                " bytes for fsctl parms at "
                FORMAT_LPVOID ".\n", FsctlParmSize, (LPVOID) FsctlParms));
    }

    FsctlParms->Type = TRANSACTION_REQUEST;
    FsctlParms->Size = FsctlParmSize;
    FsctlParms->Version = TRANSACTION_VERSION;
    FsctlParms->NameLength = APIEXTR-sizeof(WCHAR);
    FsctlParms->NameOffset = sizeof(LMR_TRANSACTION);
    NetpMoveMemory(
                NetpPointerPlusSomeBytes(
                        FsctlParms,
                        sizeof(LMR_TRANSACTION)),      // dest
                REM_APITXT,                            // src (always UNICODE)
                APIEXTR-sizeof(WCHAR));                // len (don't copy null)

    FsctlParms->ResponseExpected = TRUE;
    FsctlParms->Timeout = REM_API_TIMEOUT;     // Timeout time in milliseconds.
    FsctlParms->SetupWords = 0;
    FsctlParms->SetupOffset = 0;
    FsctlParms->MaxSetup = 0;

    FsctlParms->ParmLength = SendParmLen;
    FsctlParms->ParmPtr = SendParmPtr;
    FsctlParms->MaxRetParmLength = RetParmLen;
    NetpAssert(SendParmPtr == RetParmPtr);

    FsctlParms->DataLength = SendDataLen;
    FsctlParms->DataPtr = SendDataPtr;

    FsctlParms->MaxRetDataLength = InputRetDataLen;
    FsctlParms->RetDataPtr = RetDataPtr;

    //
    // Do the FSCTL!
    //
    Status = NetpRdrFsControlTree(
                TreeConnName,                      // tree connect name
                TransportName,                     // Transport name.
                USE_IPC,                           // Connection type
                FSCTL_LMR_TRANSACT,                // fsctl function code
                NULL,                              // security descriptor
                FsctlParms,                        // input buffer
                FsctlParmSize,                     // input buffer length
                FsctlParms,                        // output buffer
                FsctlParmSize,                     // output buffer length
                NoPermissionRequired);

    if (Status == ERROR_BAD_NET_NAME) {
        Status = NERR_BadTransactConfig;
    }
    if (RxpFatalErrorCode(Status)) {
        IF_DEBUG(TRANSACT) {
            NetpKdPrint(( PREFIX_NETAPI
                    "RxpTransactSmb: returning fatal status="
                    FORMAT_API_STATUS ".\n", Status));
        }
        NetpMemoryFree(FsctlParms);
        NetpMemoryFree(TreeConnName);
        return (Status);
    }

//
// MOD 06/11/91 RLF
// Return the received data length in *RetDataLen
//
    *RetDataLen = FsctlParms->MaxRetDataLength;
    NetpAssert( *RetDataLen <= MAX_TRANSACT_RET_DATA_SIZE );
//
// MOD 06/11/91 RLF
//

    NetpMemoryFree(FsctlParms);
    NetpMemoryFree(TreeConnName);

#else // def CDEBUG

    {
        DWORD ApiNumber;

        ApiNumber = (DWORD) SmbGetUshort((LPWORD) SendParmPtr);
        IF_DEBUG(TRANSACT) {
            NetpKdPrint(( PREFIX_NETAPI
                    "RxpTransactSmb: pretending success for API "
                    FORMAT_DWORD ".\n", ApiNumber));
        }
        SmbPutUshort((LPWORD) RetParmPtr, (WORD) NERR_Success);

        switch (ApiNumber) {
        case API_NetRemoteTOD :
            {
                UCHAR BogusTime[] = {
                        0xD0, 0xAE, 0xB2, 0x28,   // 21-Aug-1991 (6:20PM)
                        0x44, 0x33, 0x22, 0x11,   // msec (anything)
                        3,                        // hours
                        30,                       // minutes
                        15,                       // seconds
                        55,                       // hundredths of seconds
                        0xFF, 0xFF,               // timezone (unknown)
                        0xA6, 0x00,               // clock interval (60 Hz)
                        10,                       // day
                        1,                        // month
                        0xC7, 0x07,               // year
                        4};                       // weekday
                NetpAssert(RetDataPtr != NULL);
                NetpAssert(InputRetDataLen != 0);
                NetpMoveMemory(
                            RetDataPtr,                       // dest
                            BogusTime,                        // src (bogus)
                            InputRetDataLen);                      // len
                break;
            }
        case API_WServerGetInfo :
            {
                LPVOID FixedDataEnd = NetpPointerPlusSomeBytes(
                                RetDataPtr,
                                sizeof(SERVER_INFO_100));
                LPBYTE LastString = NetpPointerPlusSomeBytes(
                                RetDataPtr,
                                InputRetDataLen);
                LPSERVER_INFO_100 p = RetDataPtr;

                NetpAssert(RetDataPtr != NULL);
                NetpAssert(InputRetDataLen != 0);
                p->sv100_name = (LPTSTR) TEXT("\\\\bogus\\name");
                if (NetpPackString(
                                & p->sv100_name,                // in out
                                FixedDataEnd,                   // in
                                & LastString) == 0) {           // in out
                    NetpBreakPoint();
                    return (NERR_InternalError);
                }
                break;
            }
        }

    }

#endif // def CDEBUG

    Status = (DWORD) SmbGetUshort((LPWORD) RetParmPtr);

    IF_DEBUG(TRANSACT) {
        NetpKdPrint(( PREFIX_NETAPI
                "RxpTransactSmb: returning status="
                FORMAT_API_STATUS ".\n", Status));
        NetpKdPrint(( PREFIX_NETAPI
                "RxpTransactSmb: RetParm at " FORMAT_LPVOID
                ", len=" FORMAT_DWORD " (partial):\n",
                (LPVOID) RetParmPtr, RetParmLen));
        if (RetParmPtr != NULL) {
            NetpDbgHexDump(RetParmPtr, NetpDbgReasonable(RetParmLen));
        }
        NetpKdPrint(( PREFIX_NETAPI
                "RxpTransactSmb: (new) RetData at " FORMAT_LPVOID ", "
                "len=" FORMAT_DWORD " (partial):\n",
                (LPVOID) RetDataPtr, InputRetDataLen));
        if (RetDataPtr != NULL) {
            NetpDbgHexDump(RetDataPtr, NetpDbgReasonable(InputRetDataLen));
        }
    }
    return (Status);

} // RxpTransactSmb
