You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2612 lines
66 KiB
2612 lines
66 KiB
/*++
|
|
|
|
Copyright (c) 1998-1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
routing\netsh\shell\shell.c
|
|
|
|
Abstract:
|
|
|
|
The command shell.
|
|
|
|
Revision History:
|
|
|
|
Anand Mahalingam 7/6/98 Created
|
|
Dave Thaler 99 Updated
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
|
|
#undef EXTRA_DEBUG
|
|
|
|
//
|
|
// Define this when we allow a -r option to do remote config
|
|
//
|
|
#define ALLOW_REMOTES
|
|
|
|
#define DEFAULT_STARTUP_CONTEXT L"netsh"
|
|
|
|
WCHAR RtmonPrompt[MAX_CMD_LEN];
|
|
WCHAR g_pwszContext[MAX_CMD_LEN] = DEFAULT_STARTUP_CONTEXT;
|
|
WCHAR g_pwszNewContext[MAX_CMD_LEN] = DEFAULT_STARTUP_CONTEXT;
|
|
LPWSTR g_pwszRouterName = NULL;
|
|
LPWSTR g_pwszRememberedConnection = NULL;
|
|
HANDLE g_hModule;
|
|
BOOL g_bVerbose = FALSE;
|
|
BOOL g_bInteractive = FALSE;
|
|
DWORD g_dwContextArgCount;
|
|
DWORD g_dwTotalArgCount;
|
|
BOOL g_bDone = FALSE;
|
|
HANDLE g_hLogFile = NULL;
|
|
|
|
BOOL
|
|
WINAPI
|
|
HandlerRoutine(
|
|
DWORD dwCtrlType // control signal type
|
|
);
|
|
|
|
//
|
|
// If quiet, "Ok" and other informational messages will be suppressed.
|
|
//
|
|
BOOL g_bQuiet = TRUE;
|
|
|
|
CMD_ENTRY g_UbiqCmds[] =
|
|
{
|
|
CREATE_CMD_ENTRY( DUMP, HandleUbiqDump),
|
|
CREATE_CMD_ENTRY( HELP1, HandleUbiqHelp),
|
|
CREATE_CMD_ENTRY( HELP2, HandleUbiqHelp),
|
|
};
|
|
|
|
ULONG g_ulNumUbiqCmds = sizeof(g_UbiqCmds)/sizeof(CMD_ENTRY);
|
|
|
|
CMD_ENTRY g_ShellCmds[] =
|
|
{
|
|
// CREATE_CMD_ENTRY( DUMP, HandleShellDump),
|
|
// CREATE_CMD_ENTRY( HELP1, HandleShellHelp),
|
|
// CREATE_CMD_ENTRY( HELP2, HandleShellHelp),
|
|
CREATE_CMD_ENTRY( LOAD, HandleShellLoad),
|
|
CREATE_CMD_ENTRY_EX(QUIT, HandleShellExit, CMD_FLAG_INTERACTIVE),
|
|
CREATE_CMD_ENTRY_EX(BYE, HandleShellExit, CMD_FLAG_INTERACTIVE),
|
|
CREATE_CMD_ENTRY_EX(EXIT, HandleShellExit, CMD_FLAG_INTERACTIVE),
|
|
CREATE_CMD_ENTRY_EX(FLUSH, HandleShellFlush, CMD_FLAG_INTERACTIVE),
|
|
CREATE_CMD_ENTRY_EX(SAVE, HandleShellSave, CMD_FLAG_INTERACTIVE),
|
|
CREATE_CMD_ENTRY_EX(COMMIT, HandleShellCommit, CMD_FLAG_INTERACTIVE),
|
|
CREATE_CMD_ENTRY_EX(UNCOMMIT, HandleShellUncommit,CMD_FLAG_INTERACTIVE),
|
|
CREATE_CMD_ENTRY_EX(ALIAS, HandleShellAlias, CMD_FLAG_INTERACTIVE),
|
|
CREATE_CMD_ENTRY_EX(UNALIAS, HandleShellUnalias, CMD_FLAG_INTERACTIVE),
|
|
CREATE_CMD_ENTRY_EX(UPLEVEL, HandleShellUplevel, CMD_FLAG_INTERACTIVE),
|
|
CREATE_CMD_ENTRY_EX(PUSHD, HandleShellPushd, CMD_FLAG_INTERACTIVE),
|
|
CREATE_CMD_ENTRY_EX(POPD, HandleShellPopd, CMD_FLAG_INTERACTIVE),
|
|
};
|
|
|
|
ULONG g_ulNumShellCmds = sizeof(g_ShellCmds)/sizeof(CMD_ENTRY);
|
|
|
|
CMD_ENTRY g_ShellAddCmdTable[] = {
|
|
CREATE_CMD_ENTRY_EX(ADD_HELPER, HandleAddHelper, CMD_FLAG_LOCAL),
|
|
};
|
|
|
|
CMD_ENTRY g_ShellSetCmdTable[] = {
|
|
CREATE_CMD_ENTRY_EX(SET_MACHINE, HandleSetMachine,CMD_FLAG_ONLINE),
|
|
CREATE_CMD_ENTRY_EX(SET_MODE, HandleSetMode, CMD_FLAG_INTERACTIVE),
|
|
CREATE_CMD_ENTRY_EX(SET_FILE, HandleSetFile, CMD_FLAG_INTERACTIVE),
|
|
};
|
|
|
|
CMD_ENTRY g_ShellDelCmdTable[] = {
|
|
CREATE_CMD_ENTRY_EX(DEL_HELPER, HandleDelHelper, CMD_FLAG_LOCAL),
|
|
};
|
|
|
|
CMD_ENTRY g_ShellShowCmdTable[] = {
|
|
CREATE_CMD_ENTRY_EX(SHOW_ALIAS, HandleShowAlias, 0),
|
|
CREATE_CMD_ENTRY_EX(SHOW_HELPER, HandleShowHelper, 0),
|
|
CREATE_CMD_ENTRY_EX(SHOW_MODE, HandleShowMode, CMD_FLAG_INTERACTIVE),
|
|
};
|
|
|
|
CMD_GROUP_ENTRY g_ShellCmdGroups[] =
|
|
{
|
|
CREATE_CMD_GROUP_ENTRY(GROUP_ADD, g_ShellAddCmdTable),
|
|
CREATE_CMD_GROUP_ENTRY(GROUP_DELETE, g_ShellDelCmdTable),
|
|
CREATE_CMD_GROUP_ENTRY(GROUP_SET, g_ShellSetCmdTable),
|
|
CREATE_CMD_GROUP_ENTRY(GROUP_SHOW, g_ShellShowCmdTable),
|
|
};
|
|
|
|
ULONG g_ulNumGroups = sizeof(g_ShellCmdGroups)/sizeof(CMD_GROUP_ENTRY);
|
|
|
|
DWORD
|
|
ParseCommand(
|
|
IN PLIST_ENTRY pleEntry,
|
|
IN BOOL bAlias
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This converts any multi-token arguments into separate arg entries.
|
|
If bAlias is set, it also expands any args which are aliases.
|
|
|
|
Arguments:
|
|
|
|
ple - Pointer to the argument.
|
|
bAlias - To look for alias or not.
|
|
|
|
Called by: ConvertBufferToArgList(), ProcessCommand()
|
|
|
|
Return Value:
|
|
|
|
ERROR_NOT_ENOUGH_MEMORY, NO_ERROR
|
|
|
|
--*/
|
|
{
|
|
DWORD dwErr = NO_ERROR, dwLen , i;
|
|
LPWSTR pw1, pw2, pwszAlias;
|
|
PLIST_ENTRY ple, ple1, pleTmp, plePrev, pleAlias;
|
|
PARG_ENTRY pae, paeArg;
|
|
WCHAR wcTmp;
|
|
|
|
paeArg = CONTAINING_RECORD(pleEntry, ARG_ENTRY, le);
|
|
|
|
if (! paeArg->pwszArg)
|
|
{
|
|
return NO_ERROR;
|
|
}
|
|
|
|
pw1 = paeArg->pwszArg;
|
|
|
|
//
|
|
// Get each argument in the command. Argument within " must
|
|
// be retained as is. The token delimiters are ' ' and '='
|
|
//
|
|
|
|
for (plePrev = pleEntry ; ; )
|
|
{
|
|
// Skip leading whitespace
|
|
for(; *pw1 && *pw1 != L'#' && (*pw1 == L' ' || *pw1 == L'\t'); pw1++);
|
|
|
|
// If it starts with a #, it's the same as an empty string
|
|
if (*pw1 == L'#')
|
|
*pw1 = L'\0';
|
|
|
|
// If it's an empty string, we're done
|
|
if (!(*pw1))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (*pw1 is L'"')
|
|
{
|
|
for (pw2 = pw1 + 1; *pw2 && *pw2 != L'"'; pw2++);
|
|
if (*pw2)
|
|
{
|
|
pw2++;
|
|
}
|
|
}
|
|
else if (*pw1 is L'=')
|
|
{
|
|
pw2 = pw1 + 1;
|
|
}
|
|
else
|
|
{
|
|
for(pw2 = pw1 + 1; *pw2 && *pw2 != L' ' && *pw2 != L'=' ; pw2++);
|
|
}
|
|
|
|
//
|
|
// Add the new argument to the list.
|
|
//
|
|
|
|
pae = MALLOC(sizeof(ARG_ENTRY));
|
|
|
|
if (pae is NULL)
|
|
{
|
|
dwErr = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
|
|
ple = &(pae->le);
|
|
|
|
ple->Flink = plePrev->Flink;
|
|
ple->Blink = plePrev;
|
|
plePrev->Flink = ple;
|
|
ple->Flink->Blink = ple;
|
|
|
|
plePrev = ple;
|
|
|
|
wcTmp = *pw2;
|
|
*pw2 = L'\0';
|
|
|
|
//
|
|
// The argument could be an alias. If so replace it by
|
|
// the original string.
|
|
//
|
|
|
|
if (bAlias)
|
|
{
|
|
ATLookupAliasTable(pw1, &pwszAlias);
|
|
}
|
|
else
|
|
{
|
|
pwszAlias = NULL;
|
|
}
|
|
|
|
if (pwszAlias)
|
|
{
|
|
pw1 = pwszAlias;
|
|
dwLen = wcslen(pwszAlias) + 1;
|
|
}
|
|
else
|
|
{
|
|
dwLen = (DWORD)(pw2 - pw1 + 1);
|
|
}
|
|
|
|
pae->pwszArg = MALLOC(dwLen * sizeof(WCHAR));
|
|
|
|
if (pae->pwszArg is NULL)
|
|
{
|
|
dwErr = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
|
|
// Convert /? and -? to just ?
|
|
if (!wcscmp(pw1, L"/?") or !wcscmp(pw1, L"-?"))
|
|
{
|
|
pw1++;
|
|
}
|
|
|
|
wcscpy(pae->pwszArg, pw1);
|
|
|
|
*pw2 = wcTmp;
|
|
pw1 = pw2;
|
|
}
|
|
|
|
if (dwErr is NO_ERROR)
|
|
{
|
|
// Free the argument
|
|
FREE(paeArg->pwszArg);
|
|
pleEntry->Blink->Flink = pleEntry->Flink;
|
|
pleEntry->Flink->Blink = pleEntry->Blink;
|
|
FREE(paeArg);
|
|
}
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
DWORD
|
|
WINAPI
|
|
UpdateNewContext(
|
|
IN OUT LPWSTR pwszBuffer,
|
|
IN LPCWSTR pwszNewToken,
|
|
IN DWORD dwArgs
|
|
)
|
|
/*++
|
|
pwszBuffer - a static buffer (should be g_pwszNewContext)
|
|
currently this can't be dynamic since the context buffer
|
|
would have to be an IN/OUT parameter to this function
|
|
and hence to all monitor Entry points.
|
|
--*/
|
|
{
|
|
DWORD dwErr;
|
|
PLIST_ENTRY pleHead, pleNode;
|
|
PARG_ENTRY pae;
|
|
|
|
// Convert buffer to list
|
|
|
|
dwErr = ConvertBufferToArgList( &pleHead, pwszBuffer );
|
|
|
|
if (dwErr)
|
|
{
|
|
return dwErr;
|
|
}
|
|
|
|
// Locate in list
|
|
|
|
for (pleNode = pleHead->Blink; dwArgs>1; pleNode=pleNode->Blink)
|
|
{
|
|
if (pleNode->Blink isnot pleHead)
|
|
{
|
|
pae = CONTAINING_RECORD(pleNode->Blink, ARG_ENTRY, le);
|
|
if (!wcscmp(pae->pwszArg,L"="))
|
|
{
|
|
pleNode=pleNode->Blink; // back up over =
|
|
if (pleNode->Blink isnot pleHead)
|
|
{
|
|
pleNode=pleNode->Blink; // back up over tag too
|
|
}
|
|
}
|
|
}
|
|
|
|
dwArgs--;
|
|
}
|
|
|
|
// Update in list
|
|
|
|
pae = CONTAINING_RECORD(pleNode, ARG_ENTRY, le);
|
|
if (pae->pwszArg)
|
|
{
|
|
FREE(pae->pwszArg);
|
|
}
|
|
pae->pwszArg = MALLOC((wcslen(pwszNewToken)+1) * sizeof(WCHAR));
|
|
if (pae->pwszArg is NULL)
|
|
{
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
wcscpy(pae->pwszArg, pwszNewToken);
|
|
|
|
// Convert list to buffer
|
|
|
|
dwErr = ConvertArgListToBuffer( pleHead, pwszBuffer );
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
DWORD
|
|
GetNewContext(
|
|
IN LPCWSTR pwszArgument,
|
|
OUT LPWSTR *ppwszNewContext,
|
|
OUT BOOL *pbContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Based on the first command argument and the current context,
|
|
determines the context for the new command.
|
|
|
|
Arguments:
|
|
|
|
pwszArgument - First argument in the command line.
|
|
ppwszNewContext - Pointer to the new context.
|
|
pbContext - Is it a new context ?
|
|
|
|
Return Value:
|
|
|
|
ERROR_NOT_ENOUGH_MEMORY, NO_ERROR
|
|
|
|
--*/
|
|
{
|
|
LPWSTR pwszNewContext, pwcToken, pw1, pwszArgumentCopy, pwszArgumentPtr;
|
|
DWORD dwSize;
|
|
|
|
pwszArgumentCopy = _wcsdup(pwszArgument);
|
|
|
|
if ( pwszArgumentCopy is NULL )
|
|
{
|
|
*pbContext = FALSE;
|
|
|
|
PrintMessageFromModule(g_hModule, MSG_NOT_ENOUGH_MEMORY);
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
pwszArgumentPtr = pwszArgumentCopy;
|
|
|
|
//
|
|
// New Context cannot be longer than the combined lengths
|
|
// of pwszArgument and g_pwszContext.
|
|
//
|
|
|
|
dwSize = wcslen(pwszArgumentCopy) + wcslen(g_pwszContext) + 2;
|
|
|
|
pwszNewContext = MALLOC(dwSize * sizeof(WCHAR));
|
|
|
|
if (pwszNewContext is NULL)
|
|
{
|
|
*pbContext = FALSE;
|
|
|
|
PrintMessageFromModule(g_hModule, MSG_NOT_ENOUGH_MEMORY);
|
|
free(pwszArgumentPtr);
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
if (pwszArgumentCopy[0] is L'\\')
|
|
{
|
|
//
|
|
// The context is an absolute one.
|
|
//
|
|
|
|
pwszNewContext[0] = L'\0';
|
|
pwszArgumentCopy++;
|
|
*pbContext = TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Context is relative to current.
|
|
//
|
|
|
|
wcscpy(pwszNewContext,g_pwszContext);
|
|
}
|
|
|
|
if ((pwcToken = wcstok(pwszArgumentCopy, L"\\" )) is NULL)
|
|
{
|
|
*ppwszNewContext = pwszNewContext;
|
|
free(pwszArgumentPtr);
|
|
return NO_ERROR;
|
|
}
|
|
|
|
do
|
|
{
|
|
if (_wcsicmp(pwcToken, L"..") == 0)
|
|
{
|
|
//
|
|
// Go back one level. If already at root, ignore.
|
|
//
|
|
|
|
if (_wcsicmp(pwszNewContext,L"\\") == 0)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
pw1 = wcsrchr(pwszNewContext,L'\\');
|
|
if (pw1)
|
|
{
|
|
*pw1 = L'\0';
|
|
}
|
|
}
|
|
|
|
*pbContext = TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// add this level to context
|
|
//
|
|
|
|
wcscat(pwszNewContext,L"\\");
|
|
wcscat(pwszNewContext,pwcToken);
|
|
*pbContext = TRUE;
|
|
|
|
}
|
|
|
|
} while ((pwcToken = wcstok((LPWSTR)NULL, L"\\" )) != NULL);
|
|
|
|
*ppwszNewContext = pwszNewContext;
|
|
|
|
free(pwszArgumentPtr);
|
|
return NO_ERROR;
|
|
}
|
|
|
|
DWORD
|
|
AppendString(
|
|
IN OUT LPWSTR *ppwszBuffer,
|
|
IN LPCWSTR pwszString
|
|
)
|
|
{
|
|
LPWSTR pwszNewBuffer;
|
|
DWORD dwLen;
|
|
|
|
if (*ppwszBuffer is NULL) {
|
|
dwLen = wcslen(pwszString) + 1;
|
|
|
|
pwszNewBuffer = MALLOC( dwLen * sizeof(WCHAR));
|
|
|
|
if (pwszNewBuffer) {
|
|
pwszNewBuffer[0] = 0;
|
|
}
|
|
} else {
|
|
dwLen = wcslen(*ppwszBuffer) + wcslen(pwszString) + 1;
|
|
|
|
pwszNewBuffer = REALLOC( *ppwszBuffer, dwLen * sizeof(WCHAR));
|
|
}
|
|
|
|
if (!pwszNewBuffer)
|
|
{
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
*ppwszBuffer = pwszNewBuffer;
|
|
wcscat(pwszNewBuffer, pwszString);
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
VOID
|
|
ConvertArgArrayToBuffer(
|
|
IN DWORD dwArgCount,
|
|
IN LPCWSTR *argv,
|
|
OUT LPWSTR *ppwszBuffer
|
|
)
|
|
{
|
|
DWORD i;
|
|
|
|
#ifdef EXTRA_DEBUG
|
|
PRINT1("In ConvertArgArrayToBuffer:");
|
|
for( i = 0; i < dwArgCount; i++)
|
|
{
|
|
PRINT(argv[i]);
|
|
}
|
|
#endif
|
|
|
|
// Initial string to empty
|
|
*ppwszBuffer = NULL;
|
|
|
|
for (i=0; i<dwArgCount; i++)
|
|
{
|
|
if (i)
|
|
{
|
|
AppendString(ppwszBuffer, L" ");
|
|
}
|
|
|
|
if (wcschr(argv[i], L' '))
|
|
{
|
|
AppendString(ppwszBuffer, L"\"");
|
|
AppendString(ppwszBuffer, argv[i]);
|
|
AppendString(ppwszBuffer, L"\"");
|
|
}
|
|
else
|
|
{
|
|
AppendString(ppwszBuffer, argv[i]);
|
|
}
|
|
}
|
|
|
|
#ifdef EXTRA_DEBUG
|
|
PRINT1("At end of ConvertArgArrayToBuffer:");
|
|
PRINT(*ppwszBuffer);
|
|
#endif
|
|
}
|
|
|
|
VOID
|
|
SetContext(
|
|
IN LPCWSTR wszNewContext
|
|
)
|
|
{
|
|
wcscpy(g_pwszContext, wszNewContext);
|
|
_wcslwr(g_pwszContext);
|
|
}
|
|
|
|
BOOL
|
|
IsImmediate(
|
|
IN DWORD dwCmdFlags,
|
|
IN DWORD dwRemainingArgs
|
|
)
|
|
/*++
|
|
Description:
|
|
Determines whether a command was "immediate". A command is immediate
|
|
if the context in which it exists was the current context in the
|
|
shell.
|
|
Returns:
|
|
TRUE if command is "immediate", FALSE if not
|
|
--*/
|
|
{
|
|
if (!(dwCmdFlags & CMD_FLAG_PRIVATE))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// One way to tell is to get the current context arg count,
|
|
// the total arg count, and the remaining arg count.
|
|
|
|
return (g_dwContextArgCount + dwRemainingArgs is g_dwTotalArgCount);
|
|
}
|
|
|
|
DWORD
|
|
ProcessHelperCommand2(
|
|
IN PCNS_CONTEXT_ATTRIBUTES pContext,
|
|
IN DWORD dwArgCount,
|
|
IN OUT LPWSTR *argv,
|
|
IN DWORD dwDisplayFlags,
|
|
OUT BOOL *pbDone
|
|
)
|
|
{
|
|
PNS_CONTEXT_ENTRY_FN pfnEntryPt;
|
|
DWORD dwRes, dwIdx;
|
|
LPWSTR pwszNewContext = NULL;
|
|
LPWSTR pwszOrigContext = NULL;
|
|
|
|
if (pContext->dwFlags & ~dwDisplayFlags)
|
|
{
|
|
return ERROR_CMD_NOT_FOUND;
|
|
}
|
|
|
|
pfnEntryPt = (!pContext->pReserved) ? NULL : ((PNS_PRIV_CONTEXT_ATTRIBUTES)pContext->pReserved)->pfnEntryFn;
|
|
|
|
// If arg was abbreviated, replace argv[0] with expanded name
|
|
if (wcscmp(argv[0], pContext->pwszContext))
|
|
{
|
|
pwszOrigContext = argv[0];
|
|
|
|
argv[0] = pContext->pwszContext;
|
|
}
|
|
|
|
ConvertArgArrayToBuffer(dwArgCount, argv, &pwszNewContext);
|
|
if (pwszNewContext)
|
|
{
|
|
wcsncpy(g_pwszNewContext, pwszNewContext, MAX_CMD_LEN);
|
|
|
|
//
|
|
// this is something of a hack - we put the NULL 100 chars before the end of the buffer to prevent a buffer overrun later
|
|
// in UpdateNewContext: 190933 netsh hit an AV on netsh!._wcsnicmp
|
|
//
|
|
g_pwszNewContext[ MAX_CMD_LEN - 100 ] = 0;
|
|
|
|
FREE(pwszNewContext);
|
|
pwszNewContext = NULL;
|
|
}
|
|
|
|
//
|
|
// Call the entry point of helper.
|
|
//
|
|
|
|
if (pfnEntryPt)
|
|
{
|
|
dwRes = (*pfnEntryPt)(g_pwszRouterName,
|
|
argv, // + 1,
|
|
dwArgCount, // - 1,
|
|
dwDisplayFlags,
|
|
NULL,
|
|
g_pwszNewContext);
|
|
}
|
|
else
|
|
{
|
|
dwRes = GenericMonitor(pContext,
|
|
g_pwszRouterName,
|
|
argv, // + 1,
|
|
dwArgCount, // - 1,
|
|
dwDisplayFlags,
|
|
NULL,
|
|
g_pwszNewContext);
|
|
}
|
|
|
|
if (pwszOrigContext)
|
|
{
|
|
argv[0] = pwszOrigContext;
|
|
}
|
|
|
|
if (dwRes isnot NO_ERROR)
|
|
{
|
|
if (dwRes is ERROR_CONTEXT_SWITCH)
|
|
{
|
|
if (!(dwDisplayFlags & CMD_FLAG_INTERACTIVE))
|
|
{
|
|
LPWSTR *argv2 = MALLOC((dwArgCount+1) * sizeof(LPWSTR));
|
|
|
|
if (argv2 is NULL)
|
|
{
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
CopyMemory(argv2, argv, dwArgCount * sizeof(LPWSTR));
|
|
|
|
argv2[dwArgCount] = MALLOC((wcslen(CMD_HELP2)+1) * sizeof(WCHAR));
|
|
|
|
if (argv2[dwArgCount] is NULL)
|
|
{
|
|
dwRes = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
else
|
|
{
|
|
wcscpy(argv2[dwArgCount], CMD_HELP2);
|
|
|
|
g_dwTotalArgCount = dwArgCount+1;
|
|
|
|
dwRes = ProcessHelperCommand2(pContext,
|
|
dwArgCount+1,
|
|
argv2,
|
|
dwDisplayFlags,
|
|
pbDone);
|
|
|
|
FREE(argv2[dwArgCount]);
|
|
}
|
|
FREE(argv2);
|
|
|
|
return dwRes;
|
|
}
|
|
|
|
//
|
|
// A context switch.
|
|
//
|
|
|
|
SetContext(g_pwszNewContext);
|
|
|
|
dwRes = NO_ERROR;
|
|
}
|
|
else if (dwRes is ERROR_CONNECT_REMOTE_CONFIG)
|
|
{
|
|
PrintMessageFromModule(g_hModule, EMSG_REMOTE_CONNECT_FAILED,
|
|
g_pwszRouterName);
|
|
|
|
*pbDone = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
return dwRes;
|
|
}
|
|
|
|
DWORD
|
|
WINAPI
|
|
ProcessHelperCommand(
|
|
IN DWORD dwArgCount,
|
|
IN OUT LPWSTR *argv,
|
|
IN DWORD dwDisplayFlags,
|
|
// OUT LPWSTR *ppwszNewContext,
|
|
OUT BOOL *pbDone
|
|
)
|
|
{
|
|
PCNS_CONTEXT_ATTRIBUTES pContext;
|
|
PNS_CONTEXT_ENTRY_FN pfnEntryPt;
|
|
DWORD dwRes, dwIdx;
|
|
LPWSTR pwszNewContext = NULL;
|
|
LPWSTR pwszOrigContext = NULL;
|
|
PNS_HELPER_TABLE_ENTRY pHelper;
|
|
|
|
dwRes = GetRootContext( &pContext, &pHelper);
|
|
if (dwRes)
|
|
{
|
|
return dwRes;
|
|
}
|
|
|
|
dwRes = GetContextEntry(pHelper, argv[0], &pContext);
|
|
|
|
if (dwRes isnot NO_ERROR)
|
|
{
|
|
return ERROR_CMD_NOT_FOUND;
|
|
}
|
|
|
|
if (pContext->dwFlags & ~dwDisplayFlags)
|
|
{
|
|
return ERROR_CMD_NOT_FOUND;
|
|
}
|
|
|
|
#if 1
|
|
pfnEntryPt = (!pContext->pReserved) ? NULL : ((PNS_PRIV_CONTEXT_ATTRIBUTES)pContext->pReserved)->pfnEntryFn;
|
|
#else
|
|
dwRes = GetHelperAttributes(dwIdx, &pfnEntryPt);
|
|
|
|
if (dwRes != NO_ERROR)
|
|
{
|
|
//
|
|
// Could not find helper or could not load the DLL
|
|
//
|
|
|
|
return dwRes;
|
|
}
|
|
#endif
|
|
|
|
// If arg was abbreviated, replace argv[0] with expanded name
|
|
if (wcscmp(argv[0], pContext->pwszContext))
|
|
{
|
|
pwszOrigContext = argv[0];
|
|
|
|
argv[0] = pContext->pwszContext;
|
|
}
|
|
|
|
ConvertArgArrayToBuffer(dwArgCount, argv, &pwszNewContext);
|
|
if (pwszNewContext)
|
|
{
|
|
wcsncpy(g_pwszNewContext, pwszNewContext, MAX_CMD_LEN);
|
|
g_pwszNewContext[ MAX_CMD_LEN - 1 ] = '\0';
|
|
|
|
FREE(pwszNewContext);
|
|
pwszNewContext = NULL;
|
|
}
|
|
|
|
//
|
|
// Call the entry point of helper.
|
|
//
|
|
|
|
if (pfnEntryPt)
|
|
{
|
|
dwRes = (*pfnEntryPt)(g_pwszRouterName,
|
|
argv + 1,
|
|
dwArgCount - 1,
|
|
dwDisplayFlags,
|
|
NULL,
|
|
g_pwszNewContext);
|
|
}
|
|
else
|
|
{
|
|
dwRes = GenericMonitor(pContext,
|
|
g_pwszRouterName,
|
|
argv + 1,
|
|
dwArgCount - 1,
|
|
dwDisplayFlags,
|
|
NULL,
|
|
g_pwszNewContext);
|
|
}
|
|
|
|
if (pwszOrigContext)
|
|
{
|
|
argv[0] = pwszOrigContext;
|
|
}
|
|
|
|
if (dwRes isnot NO_ERROR)
|
|
{
|
|
if (dwRes is ERROR_CONTEXT_SWITCH)
|
|
{
|
|
if (!(dwDisplayFlags & CMD_FLAG_INTERACTIVE))
|
|
{
|
|
LPWSTR *argv2 = MALLOC((dwArgCount+1) * sizeof(LPWSTR));
|
|
|
|
if (argv2 is NULL)
|
|
{
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
CopyMemory(argv2, argv, dwArgCount * sizeof(LPWSTR));
|
|
|
|
argv2[dwArgCount] = MALLOC((wcslen(CMD_HELP2)+1) * sizeof(WCHAR));
|
|
|
|
if (argv2[dwArgCount] is NULL)
|
|
{
|
|
dwRes = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
else
|
|
{
|
|
wcscpy(argv2[dwArgCount], CMD_HELP2);
|
|
|
|
g_dwTotalArgCount = dwArgCount+1;
|
|
|
|
dwRes = ProcessHelperCommand(dwArgCount+1,
|
|
argv2,
|
|
dwDisplayFlags,
|
|
pbDone);
|
|
|
|
FREE(argv2[dwArgCount]);
|
|
}
|
|
FREE(argv2);
|
|
|
|
return dwRes;
|
|
}
|
|
|
|
//
|
|
// A context switch.
|
|
//
|
|
|
|
SetContext(g_pwszNewContext);
|
|
|
|
dwRes = NO_ERROR;
|
|
}
|
|
else if (dwRes is ERROR_CONNECT_REMOTE_CONFIG)
|
|
{
|
|
PrintMessageFromModule(g_hModule, EMSG_REMOTE_CONNECT_FAILED,
|
|
g_pwszRouterName);
|
|
|
|
*pbDone = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
return dwRes;
|
|
}
|
|
|
|
DWORD
|
|
WINAPI
|
|
ExecuteHandler(
|
|
IN HANDLE hModule,
|
|
IN CMD_ENTRY *pCmdEntry,
|
|
IN OUT LPWSTR *argv,
|
|
IN DWORD dwNumMatched,
|
|
IN DWORD dwArgCount,
|
|
IN DWORD dwFlags,
|
|
IN LPCVOID pvData,
|
|
IN LPCWSTR pwszGroupName,
|
|
OUT BOOL *pbDone)
|
|
{
|
|
DWORD dwErr = NO_ERROR;
|
|
|
|
if (((dwArgCount - dwNumMatched) == 1)
|
|
&& IsHelpToken(argv[dwNumMatched]))
|
|
{
|
|
dwErr = ERROR_SHOW_USAGE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Call the parsing routine for the command
|
|
//
|
|
dwErr = pCmdEntry->pfnCmdHandler( g_pwszRouterName,
|
|
argv,
|
|
dwNumMatched,
|
|
dwArgCount,
|
|
dwFlags,
|
|
pvData,
|
|
pbDone );
|
|
}
|
|
|
|
if (dwErr is ERROR_INVALID_SYNTAX)
|
|
{
|
|
PrintError(NULL, dwErr);
|
|
dwErr = ERROR_SHOW_USAGE;
|
|
}
|
|
|
|
switch (dwErr) {
|
|
|
|
case ERROR_SHOW_USAGE:
|
|
//
|
|
// If the only argument is a help token, just
|
|
// display the help.
|
|
//
|
|
|
|
if (NULL != pwszGroupName)
|
|
{
|
|
LPWSTR pwszGroupFullCmd = (LPWSTR)
|
|
MALLOC( ( wcslen(pwszGroupName) +
|
|
wcslen(pCmdEntry->pwszCmdToken) +
|
|
2 // for blank and NULL characters
|
|
) * sizeof(WCHAR)
|
|
);
|
|
if (NULL == pwszGroupFullCmd)
|
|
{
|
|
// we still try to print without group name
|
|
PrintMessageFromModule( hModule,
|
|
pCmdEntry->dwCmdHlpToken,
|
|
pCmdEntry->pwszCmdToken );
|
|
}
|
|
else
|
|
{
|
|
wcscpy(pwszGroupFullCmd, pwszGroupName);
|
|
wcscat(pwszGroupFullCmd, L" ");
|
|
wcscat(pwszGroupFullCmd, pCmdEntry->pwszCmdToken);
|
|
|
|
PrintMessageFromModule( hModule,
|
|
pCmdEntry->dwCmdHlpToken,
|
|
pwszGroupFullCmd );
|
|
FREE(pwszGroupFullCmd);
|
|
}
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
PrintMessageFromModule( hModule,
|
|
pCmdEntry->dwCmdHlpToken,
|
|
pCmdEntry->pwszCmdToken );
|
|
}
|
|
dwErr = NO_ERROR;
|
|
break;
|
|
|
|
case NO_ERROR:
|
|
case ERROR_SUPPRESS_OUTPUT:
|
|
break;
|
|
|
|
case ERROR_OKAY:
|
|
if (!g_bQuiet)
|
|
{
|
|
PrintMessageFromModule( NULL, MSG_OKAY);
|
|
}
|
|
dwErr = NO_ERROR;
|
|
break;
|
|
|
|
default:
|
|
PrintError(NULL, dwErr);
|
|
break;
|
|
}
|
|
|
|
if (!g_bQuiet)
|
|
{
|
|
PrintMessage( MSG_NEWLINE );
|
|
}
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
BOOL
|
|
ProcessGroupCommand(
|
|
IN DWORD dwArgCount,
|
|
IN PTCHAR *argv,
|
|
IN DWORD dwDisplayFlags,
|
|
OUT BOOL *pbDone
|
|
)
|
|
{
|
|
BOOL bFound = FALSE;
|
|
DWORD i, j, dwNumMatched, dwErr;
|
|
|
|
for(i = 0; i < g_ulNumGroups; i++)
|
|
{
|
|
if (g_ShellCmdGroups[i].dwFlags & ~dwDisplayFlags)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (MatchToken(argv[0],
|
|
g_ShellCmdGroups[i].pwszCmdGroupToken))
|
|
{
|
|
|
|
// See if it's a request for help
|
|
|
|
if ((dwArgCount < 2) || IsHelpToken(argv[1]))
|
|
{
|
|
PCNS_CONTEXT_ATTRIBUTES pContext;
|
|
|
|
dwErr = GetRootContext(&pContext, NULL);
|
|
|
|
if (dwErr is NO_ERROR)
|
|
{
|
|
dwErr = DisplayContextHelp(
|
|
pContext,
|
|
CMD_FLAG_PRIVATE,
|
|
dwDisplayFlags,
|
|
dwArgCount-2+1,
|
|
g_ShellCmdGroups[i].pwszCmdGroupToken );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Command matched entry i, so look at the table of sub commands
|
|
// for this command
|
|
//
|
|
|
|
for (j = 0; j < g_ShellCmdGroups[i].ulCmdGroupSize; j++)
|
|
{
|
|
if (g_ShellCmdGroups[i].pCmdGroup[j].dwFlags
|
|
& ~dwDisplayFlags)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (MatchCmdLine(argv,
|
|
dwArgCount,
|
|
g_ShellCmdGroups[i].pCmdGroup[j].pwszCmdToken,
|
|
&dwNumMatched))
|
|
{
|
|
bFound = TRUE;
|
|
|
|
|
|
|
|
dwErr = ExecuteHandler(g_hModule,
|
|
&g_ShellCmdGroups[i].pCmdGroup[j],
|
|
argv, dwNumMatched, dwArgCount,
|
|
dwDisplayFlags,
|
|
NULL,
|
|
g_ShellCmdGroups[i].pwszCmdGroupToken,
|
|
pbDone);
|
|
|
|
//
|
|
// quit the for(j)
|
|
//
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return bFound;
|
|
}
|
|
|
|
DWORD
|
|
LookupCommandHandler(
|
|
IN LPCWSTR pwszCmd
|
|
)
|
|
{
|
|
// Eventually, we want to look up commands in sub-contexts first,
|
|
// and end up with the global context. For now, we'll just do global.
|
|
|
|
DWORD i;
|
|
|
|
for (i = 0; i < g_ulNumShellCmds; i++)
|
|
{
|
|
if (MatchToken(pwszCmd, g_ShellCmds[i].pwszCmdToken))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
VOID
|
|
FreeArgArray(
|
|
DWORD argc,
|
|
LPWSTR *argv
|
|
)
|
|
{
|
|
DWORD i;
|
|
|
|
for (i = 0; i < argc; i++)
|
|
{
|
|
FREE(argv[i]);
|
|
}
|
|
|
|
FREE(argv);
|
|
}
|
|
|
|
DWORD
|
|
ConvertArgListToBuffer(
|
|
IN PLIST_ENTRY pleHead,
|
|
OUT LPWSTR pwszBuffer
|
|
)
|
|
{
|
|
PLIST_ENTRY ple;
|
|
PARG_ENTRY pae;
|
|
LPWSTR p = pwszBuffer;
|
|
|
|
*p = '\0';
|
|
|
|
for (ple = pleHead->Flink; ple != pleHead; ple = ple->Flink )
|
|
{
|
|
pae = CONTAINING_RECORD(ple, ARG_ENTRY, le);
|
|
|
|
if (p isnot pwszBuffer)
|
|
{
|
|
*p++ = ' ';
|
|
}
|
|
wcscpy(p, pae->pwszArg);
|
|
p += wcslen(p);
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
AppendArgument(
|
|
IN OUT LPWSTR **pargv,
|
|
IN DWORD i,
|
|
IN LPCWSTR pwszString
|
|
)
|
|
{
|
|
DWORD dwErr;
|
|
|
|
dwErr = AppendString( &(*pargv)[i], pwszString );
|
|
|
|
if ((*pargv)[i] is NULL) {
|
|
FreeArgArray(i, *pargv);
|
|
*pargv = NULL;
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
DWORD
|
|
ConvertArgListToArray(
|
|
IN PLIST_ENTRY pleContextHead,
|
|
IN PLIST_ENTRY pleHead,
|
|
OUT PDWORD pargc,
|
|
OUT LPWSTR **pargv,
|
|
OUT PDWORD pdwContextArgc
|
|
)
|
|
{
|
|
DWORD dwErr = NO_ERROR;
|
|
DWORD argc = 0, i = 0;
|
|
LPWSTR *argv = NULL, p;
|
|
BOOL bEqualTo;
|
|
PLIST_ENTRY ple;
|
|
PARG_ENTRY pae;
|
|
|
|
#ifdef EXTRA_DEBUG
|
|
if (pleHead)
|
|
{
|
|
PLIST_ENTRY ple;
|
|
|
|
PRINT1("In ConvertArgListToArray:");
|
|
for (ple = pleHead->Flink; ple != pleHead; ple = ple->Flink)
|
|
{
|
|
pae = CONTAINING_RECORD(ple, ARG_ENTRY, le);
|
|
PRINT(pae->pwszArg);
|
|
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Count tokens
|
|
|
|
if (pleContextHead)
|
|
{
|
|
for (ple = pleContextHead->Flink; ple != pleContextHead; ple = ple->Flink)
|
|
argc++;
|
|
}
|
|
*pdwContextArgc = argc;
|
|
|
|
for (ple = pleHead->Flink; ple != pleHead; ple = ple->Flink)
|
|
argc++;
|
|
|
|
do {
|
|
if (!argc)
|
|
break;
|
|
|
|
//
|
|
// Allocate space for arguments
|
|
//
|
|
|
|
argv = MALLOC(argc * sizeof(LPCWSTR));
|
|
|
|
if (argv is NULL)
|
|
{
|
|
dwErr = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
|
|
memset(argv, 0, argc * sizeof(LPCWSTR));
|
|
|
|
bEqualTo = FALSE;
|
|
|
|
//
|
|
// Copy the arguments from the list into an argv kind of
|
|
// structure. At this point, the arguments tag, '=' and
|
|
// the value are made into one argument tag=value.
|
|
//
|
|
|
|
//
|
|
// Copy context
|
|
//
|
|
|
|
i = 0;
|
|
|
|
if (pleContextHead)
|
|
{
|
|
for (ple = pleContextHead->Flink;
|
|
ple != pleContextHead;
|
|
ple = ple->Flink)
|
|
{
|
|
pae = CONTAINING_RECORD(ple, ARG_ENTRY, le);
|
|
dwErr = AppendArgument( &argv, i++, pae->pwszArg );
|
|
if ( dwErr isnot NO_ERROR ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (ple = pleHead->Flink; ple != pleHead; ple = ple->Flink )
|
|
{
|
|
pae = CONTAINING_RECORD(ple, ARG_ENTRY, le);
|
|
|
|
//
|
|
// Remove any " from the string name
|
|
//
|
|
|
|
if (pae->pwszArg[0] == L'"')
|
|
{
|
|
if (bEqualTo)
|
|
{
|
|
dwErr=AppendArgument(&argv, i-1, pae->pwszArg+1);
|
|
if ( dwErr isnot NO_ERROR ) {
|
|
break;
|
|
}
|
|
bEqualTo = FALSE;
|
|
}
|
|
else
|
|
{
|
|
dwErr=AppendArgument(&argv, i++, pae->pwszArg+1);
|
|
if (dwErr isnot NO_ERROR ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
p = argv[i-1];
|
|
if (p[wcslen(p) - 1] == L'"')
|
|
{
|
|
p[wcslen(p) - 1] = L'\0';
|
|
}
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// combine arguments of the form tag = value
|
|
//
|
|
|
|
if ((wcscmp(pae->pwszArg,L"=") == 0) || bEqualTo)
|
|
{
|
|
bEqualTo = (bEqualTo) ? FALSE : TRUE;
|
|
|
|
if (i > 0)
|
|
{
|
|
i--;
|
|
}
|
|
dwErr = AppendArgument( &argv, i++, pae->pwszArg);
|
|
if (dwErr isnot NO_ERROR ) {
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwErr = AppendArgument( &argv, i++, pae->pwszArg);
|
|
if (dwErr isnot NO_ERROR ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} while (FALSE);
|
|
|
|
#ifdef EXTRA_DEBUG
|
|
PRINT1("At end of ConvertArgListToArray:");
|
|
for( i = 0; i < argc; i++)
|
|
{
|
|
PRINT(argv[i]);
|
|
}
|
|
#endif
|
|
|
|
*pargc = i;
|
|
*pargv = argv;
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
DWORD
|
|
ConvertBufferToArgList(
|
|
PLIST_ENTRY *ppleHead,
|
|
LPCWSTR pwszBuffer
|
|
)
|
|
{
|
|
PLIST_ENTRY pleHead = NULL;
|
|
DWORD dwErr = NO_ERROR;
|
|
PARG_ENTRY pae;
|
|
|
|
#ifdef EXTRA_DEBUG
|
|
PRINT1("In ConvertBufferToArgList:");
|
|
PRINT(pwszBuffer);
|
|
#endif
|
|
|
|
do {
|
|
//
|
|
// First convert the command line to a list of tokens
|
|
//
|
|
|
|
pleHead = MALLOC(sizeof(ARG_ENTRY));
|
|
|
|
if (pleHead is NULL)
|
|
{
|
|
dwErr = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
ZeroMemory(pleHead, sizeof(ARG_ENTRY));
|
|
|
|
InitializeListHead(pleHead);
|
|
|
|
pae = MALLOC(sizeof(ARG_ENTRY));
|
|
|
|
if (pae is NULL)
|
|
{
|
|
FREE(pleHead);
|
|
pleHead = NULL;
|
|
dwErr = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
|
|
pae->pwszArg = MALLOC((wcslen(pwszBuffer)+1) * sizeof(WCHAR));
|
|
|
|
if (pae->pwszArg is NULL)
|
|
{
|
|
FREE(pleHead);
|
|
FREE(pae);
|
|
|
|
pleHead = NULL;
|
|
dwErr = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
|
|
wcscpy(pae->pwszArg, pwszBuffer);
|
|
|
|
InsertHeadList(pleHead, &(pae->le));
|
|
|
|
dwErr = ParseCommand(pleHead->Flink, FALSE);
|
|
|
|
if (dwErr isnot NO_ERROR)
|
|
{
|
|
FREE_ARG_LIST(pleHead);
|
|
pleHead = NULL;
|
|
break;
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
#ifdef EXTRA_DEBUG
|
|
if (pleHead)
|
|
{
|
|
PLIST_ENTRY ple;
|
|
|
|
PRINT1("At end of ConvertBufferToArgList:");
|
|
for (ple = pleHead->Flink; ple != pleHead; ple = ple->Flink)
|
|
{
|
|
pae = CONTAINING_RECORD(ple, ARG_ENTRY, le);
|
|
PRINT(pae->pwszArg);
|
|
|
|
}
|
|
}
|
|
#endif
|
|
|
|
*ppleHead = pleHead;
|
|
|
|
if (dwErr is ERROR_NOT_ENOUGH_MEMORY)
|
|
PrintMessageFromModule(g_hModule, MSG_NOT_ENOUGH_MEMORY);
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
DWORD
|
|
ProcessShellCommand(
|
|
IN DWORD dwArgCount,
|
|
IN OUT LPWSTR *argv,
|
|
IN DWORD dwDisplayFlags,
|
|
// OUT LPWSTR *ppwszNewContext,
|
|
OUT BOOL *pbDone
|
|
)
|
|
{
|
|
DWORD i;
|
|
|
|
for (i = 0; i < g_ulNumShellCmds; i++)
|
|
{
|
|
if (g_ShellCmds[i].dwFlags & ~dwDisplayFlags)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (MatchToken( argv[0],
|
|
g_ShellCmds[i].pwszCmdToken ))
|
|
{
|
|
return ExecuteHandler( g_hModule,
|
|
&g_ShellCmds[i],
|
|
argv,
|
|
1,
|
|
dwArgCount,
|
|
dwDisplayFlags,
|
|
NULL,
|
|
NULL,
|
|
pbDone );
|
|
|
|
}
|
|
}
|
|
|
|
return ERROR_CMD_NOT_FOUND;
|
|
}
|
|
|
|
DWORD
|
|
ProcessCommand(
|
|
IN LPCWSTR pwszCmdLine,
|
|
OUT BOOL *pbDone
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Executes command if it is for the shell or else calls the
|
|
corresponding helper routine.
|
|
|
|
Arguments:
|
|
|
|
pwszCmdLine - The command line to be executed.
|
|
|
|
Return Value:
|
|
|
|
TRUE, FALSE - (Whether to quit the program or not)
|
|
|
|
--*/
|
|
{
|
|
LPCWSTR pwszAliasString = NULL;
|
|
DWORD dwRes = NO_ERROR, i, dwLen, j;
|
|
WCHAR wszAliasString[MAX_CMD_LEN],
|
|
pwszContext[MAX_CMD_LEN];
|
|
WCHAR pwszCommandLine[MAX_CMD_LEN],*pwszNewContext;
|
|
LPCWSTR pwcAliasString, pw1,pw2,pw3;
|
|
BOOL bContext, bEqualTo, bTmp;
|
|
LPCWSTR pwszArg0, pwszArg1, pwszArg2;
|
|
PLIST_ENTRY ple, ple1, ple2, pleHead, pleTmp, pleNext;
|
|
PLIST_ENTRY pleContextHead;
|
|
PARG_ENTRY pae;
|
|
DWORD dwArgCount = 0, dwContextArgCount = 0;
|
|
LPWSTR *argv, pwcNewContext = NULL;
|
|
BOOL bShellCmd, bAlias, bFound = FALSE, dwDisplayFlags;
|
|
PCNS_CONTEXT_ATTRIBUTES pContext;
|
|
|
|
*pbDone = FALSE;
|
|
|
|
dwDisplayFlags = (g_bInteractive)? CMD_FLAG_INTERACTIVE : 0;
|
|
|
|
dwDisplayFlags |= ~CMD_FLAG_LIMIT_MASK;
|
|
|
|
// Command is executed on the local machine if router name is null
|
|
if (!g_pwszRouterName)
|
|
{
|
|
dwDisplayFlags |= CMD_FLAG_LOCAL;
|
|
}
|
|
|
|
if (g_bCommit)
|
|
{
|
|
dwDisplayFlags |= CMD_FLAG_ONLINE;
|
|
}
|
|
|
|
if (g_bVerbose)
|
|
{
|
|
PrintMessage(L"> %1!s!\n", pwszCmdLine);
|
|
}
|
|
|
|
dwRes = ConvertBufferToArgList(&pleContextHead, g_pwszContext);
|
|
|
|
if (dwRes isnot NO_ERROR)
|
|
{
|
|
*pbDone = TRUE;
|
|
return dwRes;
|
|
}
|
|
|
|
dwRes = ConvertBufferToArgList(&pleHead, pwszCmdLine);
|
|
|
|
if (dwRes isnot NO_ERROR)
|
|
{
|
|
FREE_ARG_LIST(pleContextHead);
|
|
*pbDone = TRUE;
|
|
return dwRes;
|
|
}
|
|
|
|
if (IsListEmpty(pleHead))
|
|
{
|
|
FREE_ARG_LIST(pleHead);
|
|
FREE_ARG_LIST(pleContextHead);
|
|
return NO_ERROR;
|
|
}
|
|
|
|
// Expand alias (not recursive)
|
|
|
|
dwRes = ParseCommand(pleHead->Flink, TRUE);
|
|
|
|
if (dwRes isnot NO_ERROR)
|
|
{
|
|
FREE_ARG_LIST(pleHead);
|
|
FREE_ARG_LIST(pleContextHead);
|
|
*pbDone = TRUE;
|
|
return dwRes;
|
|
}
|
|
|
|
#ifdef EXTRA_DEBUG
|
|
PRINT1("In ProcessCommand 2:");
|
|
for (ple = pleHead->Flink; ple != pleHead; ple = ple->Flink)
|
|
{
|
|
pae = CONTAINING_RECORD(ple, ARG_ENTRY, le);
|
|
PRINT(pae->pwszArg);
|
|
}
|
|
#endif
|
|
|
|
// Go through and expand any multi-tokens args into separate args
|
|
for (ple = (pleHead->Flink); ple != pleHead; ple = pleNext)
|
|
{
|
|
pleNext = ple->Flink;
|
|
|
|
dwRes = ParseCommand(ple, FALSE);
|
|
|
|
if (dwRes isnot NO_ERROR)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (dwRes isnot NO_ERROR)
|
|
{
|
|
FREE_ARG_LIST(pleHead);
|
|
FREE_ARG_LIST(pleContextHead);
|
|
*pbDone = TRUE;
|
|
return dwRes;
|
|
}
|
|
|
|
#ifdef EXTRA_DEBUG
|
|
PRINT1("In ProcessCommand 3:");
|
|
for (ple = pleHead->Flink; ple != pleHead; ple = ple->Flink)
|
|
{
|
|
pae = CONTAINING_RECORD(ple, ARG_ENTRY, le);
|
|
PRINT(pae->pwszArg);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// At this point, we should have a fully formed command,
|
|
// hopefully operable within the current context.
|
|
// The first token may be a command. If so, then the args
|
|
// will be the context followed by the rest of the tokens.
|
|
// If the first token is not a command, then the args will
|
|
// be the context followed by all the tokens.
|
|
//
|
|
|
|
if (IsListEmpty(pleHead))
|
|
{
|
|
FREE_ARG_LIST(pleHead);
|
|
FREE_ARG_LIST(pleContextHead);
|
|
return NO_ERROR;
|
|
}
|
|
|
|
pae = CONTAINING_RECORD(pleHead->Flink, ARG_ENTRY, le);
|
|
pwszArg0 = pae->pwszArg;
|
|
|
|
GetRootContext( &g_CurrentContext, &g_CurrentHelper );
|
|
|
|
do
|
|
{
|
|
// In the first context (only) we try, private commands are valid.
|
|
|
|
dwDisplayFlags |= CMD_FLAG_PRIVATE;
|
|
|
|
pContext = g_CurrentContext;
|
|
|
|
for(;;)
|
|
{
|
|
dwRes = ConvertArgListToArray( pleContextHead,
|
|
pleHead,
|
|
&dwArgCount,
|
|
&argv,
|
|
&dwContextArgCount );
|
|
|
|
g_dwTotalArgCount = dwArgCount;
|
|
g_dwContextArgCount = dwContextArgCount;
|
|
|
|
#if 1
|
|
# if 1
|
|
dwRes = ProcessHelperCommand2( pContext,
|
|
dwArgCount,
|
|
argv,
|
|
dwDisplayFlags,
|
|
pbDone );
|
|
# else
|
|
dwRes = GenericMonitor( pContext,
|
|
g_pwszRouterName,
|
|
argv,
|
|
dwArgCount,
|
|
dwDisplayFlags,
|
|
NULL,
|
|
g_pwszNewContext );
|
|
# endif
|
|
#else
|
|
{
|
|
if (!ProcessGroupCommand(dwArgCount, argv, dwDisplayFlags, pbDone))
|
|
{
|
|
//
|
|
// Having got the context and the command, see if there
|
|
// is a helper for it.
|
|
//
|
|
|
|
dwRes = ProcessHelperCommand( dwArgCount,
|
|
argv,
|
|
dwDisplayFlags,
|
|
// &pwszNewContext,
|
|
pbDone );
|
|
|
|
if (dwRes is ERROR_CMD_NOT_FOUND)
|
|
{
|
|
dwRes = ProcessShellCommand( dwArgCount,
|
|
argv,
|
|
dwDisplayFlags,
|
|
// &pwszNewContext,
|
|
pbDone );
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
FreeArgArray(dwArgCount, argv);
|
|
|
|
if (*pbDone or ((dwRes isnot ERROR_CMD_NOT_FOUND)
|
|
&& (dwRes isnot ERROR_CONTINUE_IN_PARENT_CONTEXT)))
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Make sure we don't look above "netsh"
|
|
if (pleContextHead->Flink->Flink == pleContextHead)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Delete the last element of the context list
|
|
// (Try inheriting a command from one level up)
|
|
|
|
ple = pleContextHead->Blink;
|
|
pae = CONTAINING_RECORD(ple, ARG_ENTRY, le);
|
|
if (pae->pwszArg)
|
|
FREE(pae->pwszArg);
|
|
RemoveEntryList(ple);
|
|
FREE(pae);
|
|
|
|
GetParentContext(pContext, &pContext);
|
|
|
|
dwDisplayFlags &= ~CMD_FLAG_PRIVATE;
|
|
}
|
|
|
|
#if 0
|
|
if (pwszNewContext)
|
|
{
|
|
FREE(pwszNewContext);
|
|
pwszNewContext = NULL;
|
|
}
|
|
#endif
|
|
|
|
} while (FALSE);
|
|
|
|
switch(dwRes)
|
|
{
|
|
case ERROR_OKAY:
|
|
if (!g_bQuiet)
|
|
{
|
|
PrintMessageFromModule(g_hModule, MSG_OKAY);
|
|
}
|
|
break;
|
|
|
|
case ERROR_NOT_ENOUGH_MEMORY:
|
|
PrintMessageFromModule(g_hModule, MSG_NOT_ENOUGH_MEMORY);
|
|
*pbDone = TRUE;
|
|
break;
|
|
|
|
case ERROR_CMD_NOT_FOUND:
|
|
{
|
|
LPWSTR pwszCmdLineDup = _wcsdup(pwszCmdLine);
|
|
if (!pwszCmdLineDup)
|
|
{
|
|
PrintMessageFromModule(g_hModule, MSG_NOT_ENOUGH_MEMORY);
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
if (wcslen(pwszCmdLineDup) > 256)
|
|
{
|
|
wcscpy(pwszCmdLineDup + 250, L"...");
|
|
}
|
|
|
|
PrintMessageFromModule(NULL, ERROR_CMD_NOT_FOUND, pwszCmdLineDup);
|
|
free(pwszCmdLineDup);
|
|
}
|
|
break;
|
|
|
|
case ERROR_CONTEXT_SWITCH:
|
|
{
|
|
if (!(dwDisplayFlags & CMD_FLAG_INTERACTIVE))
|
|
{
|
|
LPWSTR *argv2 = MALLOC((dwArgCount+1) * sizeof(LPWSTR));
|
|
|
|
if (argv2 is NULL)
|
|
{
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
CopyMemory(argv2, argv, dwArgCount * sizeof(LPWSTR));
|
|
|
|
argv2[dwArgCount] = MALLOC((wcslen(CMD_HELP2)+1) * sizeof(WCHAR));
|
|
|
|
if (argv2[dwArgCount] is NULL)
|
|
{
|
|
dwRes = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
else
|
|
{
|
|
wcscpy(argv2[dwArgCount], CMD_HELP2);
|
|
|
|
g_dwTotalArgCount = dwArgCount+1;
|
|
|
|
dwRes = ProcessHelperCommand(dwArgCount+1,
|
|
argv2,
|
|
dwDisplayFlags,
|
|
pbDone);
|
|
|
|
FREE(argv2[dwArgCount]);
|
|
}
|
|
FREE(argv2);
|
|
|
|
return dwRes;
|
|
}
|
|
|
|
//
|
|
// A context switch.
|
|
//
|
|
|
|
SetContext(g_pwszNewContext);
|
|
|
|
dwRes = NO_ERROR;
|
|
|
|
break;
|
|
}
|
|
|
|
case ERROR_CONNECT_REMOTE_CONFIG:
|
|
PrintMessageFromModule(g_hModule, EMSG_REMOTE_CONNECT_FAILED,
|
|
g_pwszRouterName);
|
|
|
|
g_bDone = TRUE;
|
|
break;
|
|
|
|
default:
|
|
// We don't want to print out an error in the default case here since one would have
|
|
// printed by ExecuteHandler already. Doing this will cause a duplicate message to
|
|
// be displayed.
|
|
break;
|
|
}
|
|
|
|
FREE_ARG_LIST(pleHead);
|
|
FREE_ARG_LIST(pleContextHead);
|
|
|
|
return dwRes;
|
|
}
|
|
|
|
// Append line to the full command
|
|
DWORD
|
|
AppendLineToCommand(
|
|
LPCWSTR pwszCmdLine,
|
|
DWORD dwCmdLineLen,
|
|
LPWSTR *ppwszFullCommand,
|
|
DWORD *dwFullCommandLen
|
|
)
|
|
{
|
|
LPWSTR pwszNewCommand;
|
|
DWORD dwErr = NO_ERROR;
|
|
DWORD dwLen;
|
|
|
|
// Allocate enough space to hold the full command
|
|
dwLen = *dwFullCommandLen + dwCmdLineLen;
|
|
if (*ppwszFullCommand is NULL)
|
|
{
|
|
pwszNewCommand = MALLOC( (dwLen+1) * sizeof(WCHAR) );
|
|
}
|
|
else
|
|
{
|
|
pwszNewCommand = REALLOC(*ppwszFullCommand, (dwLen+1) * sizeof(WCHAR) );
|
|
}
|
|
if (!pwszNewCommand)
|
|
{
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
// Append cmd
|
|
wcscpy(pwszNewCommand + *dwFullCommandLen, pwszCmdLine);
|
|
|
|
// Update the pointer
|
|
*ppwszFullCommand = pwszNewCommand;
|
|
*dwFullCommandLen = dwLen;
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
DWORD
|
|
MainCommandLoop(
|
|
FILE *fp,
|
|
BOOL bDisplayPrompt
|
|
)
|
|
{
|
|
LPWSTR pwszFullCommand, p, pwszCmdLine;
|
|
DWORD dwFullCommandLen, dwCmdLineLen, dwErr = NO_ERROR;
|
|
DWORD dwAnyErr = NO_ERROR;
|
|
BOOL bEof, bDone;
|
|
|
|
for ( ; ; )
|
|
{
|
|
pwszFullCommand = NULL;
|
|
dwFullCommandLen = 0;
|
|
bEof = FALSE;
|
|
|
|
if (bDisplayPrompt)
|
|
{
|
|
if (g_pwszRouterName)
|
|
{
|
|
PrintMessage(L"[%1!s!] ", g_pwszRouterName);
|
|
}
|
|
|
|
if (g_pwszContext[0] is L'\0')
|
|
{
|
|
PrintMessage(RtmonPrompt);
|
|
}
|
|
else
|
|
{
|
|
PrintMessage(L"%1!s!>",g_pwszContext);
|
|
}
|
|
}
|
|
|
|
// Get an entire command
|
|
|
|
for (;;)
|
|
{
|
|
|
|
// Get a single line, which may be \ terminated
|
|
|
|
pwszCmdLine = OEMfgets(&dwCmdLineLen, fp);
|
|
if (pwszCmdLine is NULL)
|
|
{
|
|
bEof = TRUE;
|
|
break;
|
|
}
|
|
|
|
p = pwszCmdLine + (dwCmdLineLen-1);
|
|
|
|
// Trim trailing whitespace
|
|
while ((p > pwszCmdLine) && iswspace(p[-1]))
|
|
{
|
|
*(--p) = 0;
|
|
}
|
|
|
|
if ((p > pwszCmdLine) && (p[-1] is '\\'))
|
|
{
|
|
// Strip '\\' from the end of the line
|
|
*(--p) = 0;
|
|
|
|
// Append line to the full command
|
|
AppendLineToCommand( pwszCmdLine,
|
|
(DWORD)(p-pwszCmdLine),
|
|
&pwszFullCommand,
|
|
&dwFullCommandLen );
|
|
|
|
FREE(pwszCmdLine);
|
|
continue; // Get more input
|
|
}
|
|
|
|
// Append line to the full command
|
|
AppendLineToCommand( pwszCmdLine,
|
|
(DWORD)(p-pwszCmdLine),
|
|
&pwszFullCommand,
|
|
&dwFullCommandLen );
|
|
|
|
// We're done
|
|
FREE(pwszCmdLine);
|
|
break;
|
|
}
|
|
if (bEof)
|
|
{
|
|
break;
|
|
}
|
|
|
|
dwErr = ProcessCommand(pwszFullCommand, &bDone);
|
|
if (bDone || g_bDone)
|
|
{
|
|
FREE(pwszFullCommand);
|
|
break;
|
|
}
|
|
|
|
if (dwErr)
|
|
{
|
|
dwAnyErr = dwErr;
|
|
}
|
|
|
|
FREE(pwszFullCommand);
|
|
}
|
|
|
|
return dwAnyErr;
|
|
}
|
|
|
|
DWORD
|
|
LoadScriptFile(
|
|
IN LPCWSTR pwszFileName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reads in commands from the file and processes them.
|
|
|
|
Arguments:
|
|
|
|
pwszFileName - Name of script file.
|
|
|
|
Return Value:
|
|
|
|
TRUE, FALSE
|
|
|
|
--*/
|
|
{
|
|
FILE* fp;
|
|
DWORD i, dwErr = NO_ERROR;
|
|
BOOL bOldInteractive = g_bInteractive;
|
|
BOOL bOldQuiet = g_bQuiet;
|
|
|
|
if ((fp = _wfopen(pwszFileName,L"r")) is NULL)
|
|
{
|
|
PrintMessageFromModule(g_hModule, MSG_OPEN_FAILED, pwszFileName);
|
|
return GetLastError();
|
|
}
|
|
|
|
g_bInteractive = TRUE;
|
|
g_bQuiet = TRUE;
|
|
|
|
dwErr = MainCommandLoop(fp, FALSE);
|
|
|
|
g_bInteractive = bOldInteractive;
|
|
g_bQuiet = bOldQuiet;
|
|
|
|
fclose(fp);
|
|
|
|
if (dwErr)
|
|
{
|
|
dwErr = ERROR_SUPPRESS_OUTPUT;
|
|
}
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
// This drops the IPC$ connection to the remote machine (if any).
|
|
// This function is called when we switch to a new machine, or netsh finally exits.
|
|
//
|
|
// deonb 7 Dec 2001
|
|
DWORD DisconnectFromCurrentRouter()
|
|
{
|
|
DWORD dwErr = NO_ERROR;
|
|
|
|
if (g_pwszRememberedConnection)
|
|
{
|
|
dwErr = WNetCancelConnection2(g_pwszRememberedConnection, 0, TRUE);
|
|
if (dwErr)
|
|
{
|
|
PrintError(NULL, dwErr);
|
|
}
|
|
|
|
FREE(g_pwszRememberedConnection);
|
|
g_pwszRememberedConnection = NULL;
|
|
}
|
|
return dwErr;
|
|
}
|
|
|
|
DWORD
|
|
SetMachine(
|
|
IN LPCWSTR pwszNewRouter,
|
|
IN LPCWSTR pwszUserName,
|
|
IN LPCWSTR pwszPassword
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
if (g_pwszRouterName)
|
|
{
|
|
FREE(g_pwszRouterName);
|
|
}
|
|
|
|
DisconnectFromCurrentRouter();
|
|
|
|
if (pwszNewRouter)
|
|
{
|
|
g_pwszRouterName = MALLOC((wcslen(pwszNewRouter) + 1) * sizeof(WCHAR));
|
|
|
|
if (!g_pwszRouterName)
|
|
{
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
wcscpy(g_pwszRouterName, pwszNewRouter);
|
|
}
|
|
else
|
|
{
|
|
g_pwszRouterName = NULL;
|
|
}
|
|
|
|
// Change back to root context
|
|
SetContext(DEFAULT_STARTUP_CONTEXT);
|
|
|
|
hr = UpdateVersionInfoGlobals(g_pwszRouterName, pwszUserName, pwszPassword);
|
|
if (FAILED(hr))
|
|
{
|
|
if (g_pwszRouterName)
|
|
{
|
|
PrintMessageFromModule(g_hModule, MSG_WARN_COULDNOTVERCHECKHOST, g_pwszRouterName);
|
|
}
|
|
else
|
|
{
|
|
TCHAR szComputerName[MAX_PATH];
|
|
DWORD dwComputerNameLen = MAX_PATH;
|
|
GetComputerName(szComputerName, &dwComputerNameLen);
|
|
PrintMessageFromModule(g_hModule, MSG_WARN_COULDNOTVERCHECKHOST, szComputerName);
|
|
}
|
|
PrintError(NULL, hr);
|
|
}
|
|
|
|
// Also create a connection to that machine on IPC$. This eliminates the need
|
|
// to do a net use before netsh, and then specify the username & password AGAIN to the
|
|
// netsh command line.
|
|
if (g_pwszRouterName && (pwszUserName || pwszPassword) )
|
|
{
|
|
WCHAR szIPC[MAX_PATH];
|
|
NETRESOURCE NetResource;
|
|
|
|
wsprintf(szIPC, L"\\\\%s\\ipc$", g_pwszRouterName);
|
|
|
|
ZeroMemory(&NetResource, sizeof(NETRESOURCE));
|
|
NetResource.dwType = RESOURCETYPE_ANY;
|
|
NetResource.lpLocalName = NULL;
|
|
NetResource.lpProvider = NULL;
|
|
NetResource.lpRemoteName= szIPC; // OS Selects a provider
|
|
|
|
hr = WNetAddConnection2(&NetResource, pwszPassword, pwszUserName, CONNECT_COMMANDLINE | CONNECT_INTERACTIVE);
|
|
if (S_OK == hr)
|
|
{
|
|
g_pwszRememberedConnection = MALLOC(sizeof(WCHAR) * MAX_PATH);
|
|
if (!g_pwszRememberedConnection)
|
|
{
|
|
PrintMessageFromModule(NULL, ERROR_OUTOFMEMORY);
|
|
return NO_ERROR; // don't fail the set machine since it did actually work if we're at this point.
|
|
}
|
|
wcsncpy(g_pwszRememberedConnection, szIPC, MAX_PATH);
|
|
}
|
|
else
|
|
{
|
|
PrintError(NULL, hr);
|
|
}
|
|
}
|
|
return NO_ERROR;
|
|
}
|
|
|
|
void SetThreadCodePage()
|
|
{
|
|
LANGID (WINAPI *pSetThreadUILanguage)() = NULL;
|
|
HMODULE hKernel32 = NULL;
|
|
|
|
hKernel32 = LoadLibrary(L"kernel32.dll");
|
|
if (hKernel32)
|
|
{
|
|
pSetThreadUILanguage = (PVOID) GetProcAddress( hKernel32, "SetThreadUILanguage" );
|
|
|
|
// OS Platforms before WinXP doesn't support MUI command line utilities so
|
|
// we don't need to worry about it if the O/S doesn't support this API.
|
|
if (pSetThreadUILanguage)
|
|
{
|
|
(*pSetThreadUILanguage)( 0 );
|
|
}
|
|
|
|
FreeLibrary(hKernel32);
|
|
}
|
|
}
|
|
|
|
int
|
|
MainFunction(
|
|
int argc,
|
|
WCHAR *argv[]
|
|
)
|
|
{
|
|
WCHAR pwszCmdLine[MAX_CMD_LEN] = L"\0";
|
|
WCHAR pwszArgContext[MAX_CMD_LEN] = L"\0";
|
|
BOOL bOnce = FALSE, bDone = FALSE;
|
|
LPCWSTR pwszArgAlias = NULL;
|
|
LPCWSTR pwszArgScript = NULL;
|
|
DWORD dwErr = NO_ERROR, i;
|
|
LPCWSTR p;
|
|
HRESULT hr;
|
|
LPCWSTR pwszMachineName = NULL;
|
|
LPCWSTR pwszUserName = NULL;
|
|
LPCWSTR pwszPassword = NULL;
|
|
WCHAR szPasswordPrompt[MAX_PATH];
|
|
|
|
if ((g_hModule = GetModuleHandle(NULL)) is NULL)
|
|
{
|
|
PRINT1("GetModuleHandle failed");
|
|
return 1;
|
|
}
|
|
|
|
swprintf(RtmonPrompt, L"%s>", STRING_NETSH);
|
|
|
|
//
|
|
// Initialize the Alias Table
|
|
//
|
|
|
|
dwErr = ATInitTable();
|
|
|
|
if (dwErr isnot NO_ERROR)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Initialize the root helper
|
|
AddDllEntry(L"", L"netsh.exe");
|
|
|
|
//
|
|
// Load information about the helpers from the registry.
|
|
//
|
|
|
|
LoadDllInfoFromRegistry();
|
|
|
|
// Need to set the Ctrl Handler so it can catch the Ctrl C and close window events.
|
|
// This is done so the helper dlls can properly be unloaded.
|
|
//
|
|
SetConsoleCtrlHandler(HandlerRoutine,
|
|
TRUE);
|
|
|
|
//
|
|
// Set TEB's language ID to correspond to the console output code page. This
|
|
// will ensure the correct language message is displayed when FormatMessage is
|
|
// called.
|
|
//
|
|
SetThreadCodePage();
|
|
|
|
for ( i = 1; i < (DWORD) argc; i++ )
|
|
{
|
|
if (_wcsicmp(argv[i], L"-?")==0 ||
|
|
_wcsicmp(argv[i], L"-h")==0 ||
|
|
_wcsicmp(argv[i], L"?" )==0 ||
|
|
_wcsicmp(argv[i], L"/?")==0)
|
|
{
|
|
(VOID) UpdateVersionInfoGlobals(NULL, NULL, NULL);
|
|
// deonb: If this fails we want to restrict the helper from showing up in the context - ignoring
|
|
// the return value will accomplish this.
|
|
|
|
PrintMessageFromModule(g_hModule, MSG_NETSH_USAGE, argv[0]);
|
|
ProcessCommand(L"?", &bDone);
|
|
|
|
// Need to free the helper DLLs before we exit
|
|
//
|
|
FreeHelpers();
|
|
FreeDlls();
|
|
return 1;
|
|
}
|
|
|
|
if (_wcsicmp(argv[i], L"-v") == 0)
|
|
{
|
|
g_bVerbose = TRUE;
|
|
continue;
|
|
}
|
|
|
|
if (_wcsicmp(argv[i], L"-a") == 0)
|
|
{
|
|
//
|
|
// alias file
|
|
//
|
|
if (i + 1 >= (DWORD)argc)
|
|
{
|
|
PrintMessageFromModule(g_hModule, MSG_NETSH_USAGE, argv[0]);
|
|
dwErr = ERROR_INVALID_SYNTAX;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
pwszArgAlias = argv[i+1];
|
|
i++;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (_wcsicmp(argv[i], L"-c") == 0)
|
|
{
|
|
//
|
|
// starting context
|
|
//
|
|
if (i + 1 >= (DWORD)argc)
|
|
{
|
|
PrintMessageFromModule(g_hModule, MSG_NETSH_USAGE, argv[0]);
|
|
dwErr = ERROR_INVALID_SYNTAX;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
wcscpy(pwszArgContext, argv[i+1]);
|
|
i++;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (_wcsicmp(argv[i], L"-f") == 0)
|
|
{
|
|
//
|
|
// command to run
|
|
//
|
|
if (i + 1 >= (DWORD)argc)
|
|
{
|
|
PrintMessageFromModule(g_hModule, MSG_NETSH_USAGE, argv[0]);
|
|
dwErr = ERROR_INVALID_SYNTAX;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
pwszArgScript = argv[i+1];
|
|
i++;
|
|
bOnce = TRUE;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
#ifdef ALLOW_REMOTES
|
|
if (_wcsicmp(argv[i], L"-r") == 0)
|
|
{
|
|
//
|
|
// router name
|
|
//
|
|
if (i + 1 >= (DWORD)argc)
|
|
{
|
|
PrintMessageFromModule(g_hModule, MSG_NETSH_USAGE, argv[0]);
|
|
dwErr = ERROR_INVALID_SYNTAX;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (wcslen(argv[i+1]))
|
|
{
|
|
pwszMachineName = argv[i+1];
|
|
}
|
|
|
|
i++;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (_wcsicmp(argv[i], L"-u") == 0)
|
|
{
|
|
//
|
|
// user name
|
|
//
|
|
if (i + 1 >= (DWORD)argc)
|
|
{
|
|
PrintMessageFromModule(g_hModule, MSG_NETSH_USAGE, argv[0]);
|
|
dwErr = ERROR_INVALID_SYNTAX;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (wcslen(argv[i+1]))
|
|
{
|
|
pwszUserName = argv[i+1];
|
|
}
|
|
|
|
i++;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (_wcsicmp(argv[i], L"-p") == 0)
|
|
{
|
|
//
|
|
// password
|
|
//
|
|
if (i + 1 >= (DWORD)argc)
|
|
{
|
|
PrintMessageFromModule(g_hModule, MSG_NETSH_USAGE, argv[0]);
|
|
dwErr = ERROR_INVALID_SYNTAX;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (wcslen(argv[i+1]))
|
|
{
|
|
pwszPassword = argv[i+1];
|
|
}
|
|
|
|
i++;
|
|
continue;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!bOnce)
|
|
{
|
|
while (i < (DWORD)argc)
|
|
{
|
|
if (pwszCmdLine[0])
|
|
{
|
|
wcscat(pwszCmdLine, L" ");
|
|
}
|
|
|
|
p = argv[i];
|
|
|
|
if (!p[0] || wcschr(argv[i], L' '))
|
|
{
|
|
wcscat(pwszCmdLine, L"\"");
|
|
wcscat(pwszCmdLine, p);
|
|
wcscat(pwszCmdLine, L"\"");
|
|
}
|
|
else
|
|
{
|
|
wcscat(pwszCmdLine, p);
|
|
}
|
|
|
|
i++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PrintMessageFromModule(g_hModule, MSG_NETSH_USAGE, argv[0]);
|
|
dwErr = ERROR_INVALID_SYNTAX;
|
|
}
|
|
break;
|
|
}
|
|
|
|
do
|
|
{
|
|
if (dwErr isnot NO_ERROR)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (pwszMachineName)
|
|
{
|
|
if (pwszPassword && _wcsicmp(pwszPassword, L"*") == 0)
|
|
{
|
|
DWORD dwLen = 0;
|
|
PrintMessageFromModule(g_hModule, MSG_NETSH_LOGIN_PASSWORD, pwszMachineName);
|
|
if (0 != GetPasswdStr(szPasswordPrompt, MAX_PATH, &dwLen))
|
|
{
|
|
dwErr = ERROR_INVALID_SYNTAX;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
pwszPassword = szPasswordPrompt;
|
|
}
|
|
}
|
|
|
|
dwErr = SetMachine( pwszMachineName, pwszUserName, pwszPassword );
|
|
if (dwErr isnot NO_ERROR)
|
|
{
|
|
PrintMessageFromModule(g_hModule, dwErr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!g_pwszRouterName)
|
|
{
|
|
hr = UpdateVersionInfoGlobals(NULL, NULL, NULL); // Update the info for the local machine
|
|
if (FAILED(hr))
|
|
{
|
|
if (g_pwszRouterName)
|
|
{
|
|
PrintMessageFromModule(g_hModule, MSG_WARN_COULDNOTVERCHECKHOST, g_pwszRouterName);
|
|
}
|
|
else
|
|
{
|
|
TCHAR szComputerName[MAX_PATH];
|
|
DWORD dwComputerNameLen = MAX_PATH;
|
|
GetComputerName(szComputerName, &dwComputerNameLen);
|
|
PrintMessageFromModule(g_hModule, MSG_WARN_COULDNOTVERCHECKHOST, szComputerName);
|
|
}
|
|
PrintError(NULL, hr);
|
|
}
|
|
}
|
|
|
|
if (pwszArgAlias)
|
|
{
|
|
dwErr = LoadScriptFile(pwszArgAlias);
|
|
if (dwErr)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pwszArgContext[0] != L'\0')
|
|
{
|
|
// The context switch command should be processed in
|
|
// interactive mode (which is the only time a context
|
|
// switch is legal).
|
|
|
|
g_bInteractive = TRUE;
|
|
dwErr = ProcessCommand(pwszArgContext, &bDone);
|
|
g_bInteractive = FALSE;
|
|
if (dwErr)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pwszCmdLine[0] != L'\0')
|
|
{
|
|
g_bQuiet = FALSE; // Bug# 262183
|
|
dwErr = ProcessCommand(pwszCmdLine, &bDone);
|
|
break;
|
|
}
|
|
|
|
if (pwszArgScript)
|
|
{
|
|
g_bInteractive = TRUE;
|
|
dwErr = LoadScriptFile(pwszArgScript);
|
|
break;
|
|
}
|
|
|
|
g_bInteractive = TRUE;
|
|
g_bQuiet = FALSE;
|
|
|
|
// Main command loop
|
|
dwErr = MainCommandLoop(stdin, TRUE);
|
|
|
|
} while (FALSE);
|
|
|
|
//
|
|
// Clean up
|
|
//
|
|
DisconnectFromCurrentRouter();
|
|
FreeHelpers();
|
|
FreeDlls();
|
|
FreeAliasTable();
|
|
|
|
if(g_pwszRouterName)
|
|
{
|
|
FREE(g_pwszRouterName);
|
|
|
|
g_pwszRouterName = NULL;
|
|
}
|
|
|
|
// Return 1 on error, 0 if not
|
|
return (dwErr isnot NO_ERROR);
|
|
}
|
|
|
|
int _cdecl
|
|
wmain(
|
|
int argc,
|
|
WCHAR *argv[]
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The main function.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
HANDLE hStdOut;
|
|
DWORD dwRet;
|
|
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
|
WORD oldXSize;
|
|
char buff[256];
|
|
WSADATA wsaData;
|
|
HRESULT hr;
|
|
|
|
#if 0
|
|
hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
|
|
if (hStdOut is INVALID_HANDLE_VALUE)
|
|
{
|
|
PRINT1("Standard output could not be opened.");
|
|
return 0;
|
|
}
|
|
|
|
GetConsoleScreenBufferInfo(hStdOut, &csbi);
|
|
|
|
oldXSize = csbi.dwSize.X;
|
|
csbi.dwSize.X = 120;
|
|
SetConsoleScreenBufferSize(hStdOut, csbi.dwSize);
|
|
#endif
|
|
|
|
#if 0
|
|
WSAStartup(MAKEWORD(2,0), &wsaData);
|
|
if (!gethostname(buff, sizeof(buff)))
|
|
{
|
|
g_pwszRouterName = MALLOC( (strlen(buff)+1) * sizeof(WCHAR) );
|
|
swprintf(g_pwszRouterName, L"%hs", buff);
|
|
}
|
|
#endif
|
|
|
|
dwRet = MainFunction(argc, argv);
|
|
|
|
#if 0
|
|
GetConsoleScreenBufferInfo(hStdOut, &csbi);
|
|
|
|
csbi.dwSize.X = oldXSize;
|
|
SetConsoleScreenBufferSize(hStdOut, csbi.dwSize);
|
|
CloseHandle(hStdOut);
|
|
#endif
|
|
|
|
return dwRet;
|
|
}
|
|
|
|
BOOL
|
|
IsLocalCommand(
|
|
IN LPCWSTR pwszCmd,
|
|
IN DWORD dwSkipFlags
|
|
)
|
|
/*++
|
|
Arguments:
|
|
pwszCmd - string to see if it matches a command
|
|
dwSkipFlags - any commands with these flags will be ignored.
|
|
This is the opposite semantics of the "dwDisplayFlags"
|
|
parameter used elsewhere (dwSkipFlags = ~dwDisplayFlags)
|
|
--*/
|
|
{
|
|
DWORD i, dwErr;
|
|
PCNS_CONTEXT_ATTRIBUTES pContext, pSubContext;
|
|
PNS_HELPER_TABLE_ENTRY pHelper;
|
|
|
|
dwErr = GetRootContext( &pContext, &pHelper );
|
|
if (dwErr)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
for (i=0; i<g_ulNumShellCmds; i++)
|
|
{
|
|
if (!(g_ShellCmds[i].dwFlags & dwSkipFlags)
|
|
&& !_wcsicmp( pwszCmd,
|
|
g_ShellCmds[i].pwszCmdToken ))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
for (i=0; i<g_ulNumGroups; i++)
|
|
{
|
|
if (!(g_ShellCmdGroups[i].dwFlags & dwSkipFlags)
|
|
&& !_wcsicmp( pwszCmd,
|
|
g_ShellCmdGroups[i].pwszCmdGroupToken ))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
for (i=0; i<pHelper->ulNumSubContexts; i++)
|
|
{
|
|
pSubContext = (PCNS_CONTEXT_ATTRIBUTES)
|
|
(pHelper->pSubContextTable + i*pHelper->ulSubContextSize);
|
|
|
|
if (!(pSubContext->dwFlags & dwSkipFlags)
|
|
&& !_wcsicmp( pwszCmd,
|
|
pSubContext->pwszContext))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|