#include <windows.h>
#include <stdio.h>
#include <string.h>
#include "Remote.h"
#include "Server.h"

//
// This module uses mailslots to broadcast the existence of
// this remote server to allow a form of browsing for
// remote server instances.  This is disabled in the
// customer version of remote.exe, and can be disabled
// in the internal version using the /v- switch to
// remote /s.
//
// remoteds.c implements a listener that allows searching.
//

#define  INITIAL_SLEEP_PERIOD (35 * 1000)          // 35 seconds before first
#define  INITIAL_AD_RATE      (10 * 60 * 1000)     // 10 minutes between 1 & 2,
#define  MAXIMUM_AD_RATE      (120 * 60 * 1000)    // doubling until 120 minutes max


OVERLAPPED olMailslot;
HANDLE     hAdTimer = INVALID_HANDLE_VALUE;
HANDLE     hMailslot = INVALID_HANDLE_VALUE;
DWORD      dwTimerInterval;      // milliseconds
BOOL       bSynchAdOnly;
BOOL       bSendingToMailslot;
char       szMailslotName[64];    // netbios names are short
char       szSend[1024];


#define MAX_MAILSLOT_SPEWS 2
DWORD      dwMailslotErrors;


VOID
InitAd(
   BOOL IsAdvertise
   )
{
    DWORD           cb;
    PWKSTA_INFO_101 pwki101;
    LARGE_INTEGER   DueTime;

    if (IsAdvertise) {

        // Unless Win32s or Win9x support named pipe servers...

        ASSERT(OsVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT);

        // Problems with overlapped writes to a mailslot sometimes
        // cause remote.exe to zombie on exit on NT4, undebuggable
        // and unkillable because of an abandoned RDR1 IRP which
        // never completes.
        //
        // So on NT4 we only send messages at startup and shutdown
        // and send them synchronously using a nonoverlapped handle.
        //

        bSynchAdOnly = (OsVersionInfo.dwMajorVersion <= 4);

        //
        // Get currently active computername and browser/mailslot
        // domain/workgroup using one call to NetWkstaGetInfo.
        // This is unicode-only, we'll use wsprintf's %ls to
        // convert to 8-bit characters.
        //
        // remoteds.exe needs to be run on a workstation that is
        // part of the domain or workgroup of the same name,
        // and be in broadcast range, to receive our sends.
        //

        if (pfnNetWkstaGetInfo(NULL, 101, (LPBYTE *) &pwki101)) {
            printf("REMOTE: unable to get computer/domain name, not advertising.\n");
            return;
        }

        wsprintf(
            szMailslotName,
            "\\\\%ls\\MAILSLOT\\REMOTE\\DEBUGGERS",
            pwki101->wki101_langroup
            );

        wsprintf(
            szSend,
            "%ls\t%d\t%s\t%s",
            pwki101->wki101_computername,
            GetCurrentProcessId(),
            PipeName,
            ChildCmd
            );

        pfnNetApiBufferFree(pwki101);
        pwki101 = NULL;

        //
        // Broadcast mailslots are limited to 400 message bytes
        //

        szSend[399] = 0;

        if (bSynchAdOnly) {

            hMailslot =
                CreateFile(
                    szMailslotName,
                    GENERIC_WRITE,
                    FILE_SHARE_WRITE,
                    NULL,
                    OPEN_EXISTING,
                    0,
                    NULL
                    );
            if (hMailslot != INVALID_HANDLE_VALUE) {
                if ( ! WriteFile(
                           hMailslot,
                           szSend,
                           strlen(szSend) + 1,
                           &cb,
                           NULL
                           )) {
    
                    printf("REMOTE: WriteFile Failed on mailslot, error %d\n", GetLastError());
                }
            } else {
                printf("REMOTE: Failed to create mailslot, error %d\n", GetLastError());
            }


        } else {  // we can do async mailslot I/O

            //
            // Create a waitable timer and set it to fire first in
            // INITIAL_SLEEP_PERIOD milliseconds by calling the
            // completion routine AdvertiseTimerFired.  It will
            // be given an inital period of INITIAL_AD_RATE ms.
            //

            hAdTimer =
                pfnCreateWaitableTimer(
                    NULL,               // security
                    FALSE,              // bManualReset, we want auto-reset
                    NULL                // unnamed
                    );

            DueTime.QuadPart = Int32x32To64(INITIAL_SLEEP_PERIOD, -10000);
            dwTimerInterval = INITIAL_AD_RATE;

            pfnSetWaitableTimer(
                hAdTimer,
                &DueTime,
                dwTimerInterval,
                AdvertiseTimerFired,
                0,                     // arg to compl. rtn
                TRUE
                );

        }
    }
}


VOID
ShutAd(
   BOOL IsAdvertise
   )
{
    DWORD cb;
    BOOL  b;

    if (IsAdvertise) {

        if (INVALID_HANDLE_VALUE != hAdTimer) {

            pfnCancelWaitableTimer(hAdTimer);
            CloseHandle(hAdTimer);
            hAdTimer = INVALID_HANDLE_VALUE;
        }

        if (INVALID_HANDLE_VALUE != hMailslot &&
            ! bSendingToMailslot) {

            //
            // Tell any listening remoteds's we're
            // outta here.  Do this by tacking on
            // a ^B at the end of the string (as
            // in Bye).
            //

            strcat(szSend, "\x2");


            if (bSynchAdOnly) {   // overlapped handle or not?
                b = WriteFile(
                        hMailslot,
                        szSend,
                        strlen(szSend) + 1,
                        &cb,
                        NULL
                        );
            } else {
                b = WriteFileSynch(
                        hMailslot,
                        szSend,
                        strlen(szSend) + 1,
                        &cb,
                        0,
                        &olMainThread
                        );
            }

            if ( ! b ) {

                printf("REMOTE: WriteFile Failed on mailslot, error %d\n", GetLastError());
            }

        }

        if (INVALID_HANDLE_VALUE != hMailslot) {

            printf("\rREMOTE: closing mailslot...       ");
            fflush(stdout);
            CloseHandle(hMailslot);
            hMailslot = INVALID_HANDLE_VALUE;
            printf("\r                                  \r");
            fflush(stdout);
        }
    }
}


VOID
APIENTRY
AdvertiseTimerFired(
    LPVOID pArg,
    DWORD  dwTimerLo,
    DWORD  dwTimerHi
    )
{
    UNREFERENCED_PARAMETER( pArg );
    UNREFERENCED_PARAMETER( dwTimerLo );
    UNREFERENCED_PARAMETER( dwTimerHi );


    if (INVALID_HANDLE_VALUE == hMailslot) {

        hMailslot =
            CreateFile(
                szMailslotName,
                GENERIC_WRITE,
                FILE_SHARE_WRITE,
                NULL,
                OPEN_EXISTING,
                FILE_FLAG_OVERLAPPED,
                NULL
                );
    }

    if (INVALID_HANDLE_VALUE != hMailslot) {

        ZeroMemory(&olMailslot, sizeof(olMailslot));

        bSendingToMailslot = TRUE;

        if ( ! WriteFileEx(
                   hMailslot,
                   szSend,
                   strlen(szSend) + 1,
                   &olMailslot,
                   WriteMailslotCompleted
                   )) {

            bSendingToMailslot = FALSE;

            if (++dwMailslotErrors <= MAX_MAILSLOT_SPEWS) {

                DWORD dwError;
                char szErrorText[512];

                dwError = GetLastError();

                FormatMessage(
                    FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                    NULL,
                    dwError,
                    0,
                    szErrorText,
                    sizeof szErrorText,
                    NULL
                    );

                //
                // FormatMessage has put a newline at the end of szErrorText
                //

                printf(
                    "REMOTE: Advertisement failed, mailslot error %d:\n%s",
                    dwError,
                    szErrorText
                    );
            }

            //
            // Try reopening the mailslot next time, can't hurt.
            //

            CloseHandle(hMailslot);
            hMailslot = INVALID_HANDLE_VALUE;
        }
    }
}


VOID
WINAPI
WriteMailslotCompleted(
    DWORD dwError,
    DWORD cbWritten,
    LPOVERLAPPED lpO
    )
{
    LARGE_INTEGER DueTime;

    bSendingToMailslot = FALSE;

    if (dwError ||
        (strlen(szSend) + 1) != cbWritten) {

            if (++dwMailslotErrors <= MAX_MAILSLOT_SPEWS) {
                printf("REMOTE: write failed on mailslot, error %d cb %d (s/b %d)\n",
                    dwError, cbWritten, (strlen(szSend) + 1));
            }
        return;
    }

    //
    // If we succeeded in writing the mailslot, double the timer interval
    // up to the limit.
    //

    if (dwTimerInterval < MAXIMUM_AD_RATE) {

        dwTimerInterval = max(dwTimerInterval * 2, MAXIMUM_AD_RATE);

        DueTime.QuadPart = Int32x32To64(dwTimerInterval, -10000);

        if (INVALID_HANDLE_VALUE != hAdTimer) {

            pfnSetWaitableTimer(
                hAdTimer,
                &DueTime,
                dwTimerInterval,
                AdvertiseTimerFired,
                0,                     // arg to compl. rtn
                TRUE
                );
        }
    }
}