2025-11-28 00:35:46 +09:00

352 lines
11 KiB
C++

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
/*
File Replication Sample
File Replication Client Utility
FILE: FileRep.cpp
USAGE:
FileRep [server name] [remote file] [local file] <options...>
OPTIONS:
-n network_address
-p protocol_sequence
-e endpoint
-d print messages
PURPOSE: Client utility for the file replication application
FUNCTIONS: main() - binds to replication server and requests
file replication.
COMMENTS:
*/
#include "common.h"
#include <process.h>
#include <rpc.h>
#include <Ntdsapi.h>
#include "Service.h"
// header file generated by MIDL compiler
#include "FileRepClient.h"
/*
FUNCTION: PrintUsage()
PURPOSE: Prints help information.
PARAMETERS:
ProgName - name of the executable
RETURN VALUE:
none
COMMENTS:
*/
void PrintUsage(TCHAR *ProgName) {
_tprintf_s(TEXT("%s - File Replication Client Utility\n\n"), ProgName);
_tprintf_s(TEXT("Usage: %s [server name] [remote file] [local file] <options...> \n\n"), ProgName);
_tprintf_s(TEXT("Options:\n"));
_tprintf_s(TEXT(" -p protocol_sequence\n"));
_tprintf_s(TEXT(" -n network_address\n"));
_tprintf_s(TEXT(" -e endpoint\n"));
_tprintf_s(TEXT(" -d print messages\n"));
}
/*
FUNCTION: main()
PURPOSE: Main funciton for the utility. It binds to the local server
and requests file replication.
PARAMETERS:
dwArgc - number of command line arguments
lpszArgv - array of command line arguments
RETURN VALUE:
none
COMMENTS:
*/
INT _cdecl main(int argc, char **argv) {
INT i;
RPC_STATUS rpcstatus;
// Controlls printing out of messages.
BOOL bDebug = FALSE;
// Explicit binding handle to the client system service.
handle_t hFileRepClient = NULL;
// These are the default values for extablishing connections.
// Default connection is to a local server over local rpc.
RPC_STR pszProtocolSequence = (RPC_STR)TEXT("ncalrpc");
RPC_STR pszNetworkAddress = NULL;
// An empty endpoint string is used, since we are going to
// connect to the endpoint dynamically generated by the
// RPC run-time library. Server calls RpcServerUseProtseq to
// obtain a binding hadnle and a dynamic endpoint.
RPC_STR pszEndpoint = (RPC_STR)TEXT("");
RPC_STR pszUuid = NULL;
RPC_STR pszOptions = NULL;
RPC_STR pszStringBinding = NULL;
RPC_STR pszServerPrincipalName = NULL;
LPTSTR ServerName = NULL;
LPTSTR RemoteFileName = NULL;
LPTSTR LocalFileName = NULL;
INT nNumArgs;
#ifndef NO_SEC
// Quality of service structure to ensure authentication.
RPC_SECURITY_QOS SecurityQOS;
// Used in generating the principal name for the local system service.
TCHAR Name[128];
DWORD cbName = sizeof(Name);
TCHAR DomainName[128];
DWORD cbDomainName = sizeof(DomainName);
SID_NAME_USE Use;
SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
BYTE sidBuffer[100];
PSID pSID = (PSID)&sidBuffer;
#endif
// Get a common handle on the command line arguments for both
// UNICODE and ASCII
#ifdef _UNICODE
LPWSTR *szArgList = CommandLineToArgvW(GetCommandLine(), &nNumArgs);
if (NULL == szArgList) {
_tprintf_s(TEXT("FileRep main: CommandLineToArgW failed"));
exit(EXIT_FAILURE);
}
#else
char **szArgList = argv;
nNumArgs = argc;
#endif
// Check that the correct number of arguments is given.
if(nNumArgs<4){
PrintUsage(szArgList[0]);
exit(EXIT_FAILURE);
}
// Extract the required arguments.
ServerName = szArgList[1];
RemoteFileName = szArgList[2];
LocalFileName = szArgList[3];
// Allow the user to override settings with command line switches.
for (i = 4; i < nNumArgs; i++) {
// Well-formed argument switches start with '/' or '-' and are
// two characters long.
if (((*szArgList[i] == TEXT('-')) || (*szArgList[i] == TEXT('/'))) && _tcsclen(szArgList[i]) == 2) {
switch (_totlower(*(szArgList[i]+1))) {
case TEXT('p'):
// Override protocol sequence.
// Check that the next argument exists and is not a
// switch.
if(i+1 < nNumArgs && *szArgList[i+1] != TEXT('-') && *szArgList[i+1] != TEXT('/')) {
pszProtocolSequence = (RPC_STR)szArgList[++i];
}
// Otherwise we have an empty protocol sequence.
else {
// We do not allow an empty protocol sequence.
_tprintf_s(TEXT("Protocol sequence can't be \"\".\n"));
exit(EXIT_FAILURE);
}
break;
case TEXT('n'):
// Override the network address.
if(i+1 < nNumArgs && *szArgList[i+1] != TEXT('-') && *szArgList[i+1] != TEXT('/')) {
pszNetworkAddress = (RPC_STR)szArgList[++i];
}
else {
pszNetworkAddress = (RPC_STR)TEXT("");
}
break;
case TEXT('e'):
// Override the endpoint.
if(i+1 < nNumArgs && *szArgList[i+1] != TEXT('-') && *szArgList[i+1] != TEXT('/')) {
pszEndpoint = (RPC_STR)szArgList[++i];
}
else {
pszEndpoint = (RPC_STR)TEXT("");
}
break;
case TEXT('d'):
// Turn on debugging.
bDebug = TRUE;
break;
case TEXT('h'):
case TEXT('?'):
default:
PrintUsage(szArgList[0]);
exit(EXIT_SUCCESS);
}
}
else {
PrintUsage(szArgList[0]);
exit(EXIT_FAILURE);
}
}
// Concatenate the elements of
// the string binding into the proper sequence.
rpcstatus = RpcStringBindingCompose(pszUuid,
pszProtocolSequence,
pszNetworkAddress,
pszEndpoint,
pszOptions,
&pszStringBinding);
if(bDebug){
_tprintf_s(TEXT("RpcStringBindingCompose returned 0x%x\n"), rpcstatus);
_tprintf_s(TEXT("pszStringBinding = %s\n"), pszStringBinding);
}
RPC_EEINFO_EXIT_IF_FAIL(rpcstatus, TEXT("RpcStringBindingCompose"));
// Set the binding handle that will be used to bind to the server.
rpcstatus = RpcBindingFromStringBinding(pszStringBinding, &hFileRepClient);
if(bDebug) _tprintf_s(TEXT("RpcBindingFromStringBinding returned 0x%x\n"), rpcstatus);
RPC_EEINFO_EXIT_IF_FAIL(rpcstatus, TEXT("RpcBindingFromStringBinding"));
// Free the binding string.
rpcstatus = RpcStringFree(&pszStringBinding);
ASSERT(rpcstatus == RPC_S_OK);
#ifndef NO_SEC
// Specify quality of service parameters.
SecurityQOS.Version = RPC_C_SECURITY_QOS_VERSION;
SecurityQOS.Capabilities = RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH;
// Dynamic identity tracking is more efficient for a single LRPC call.
SecurityQOS.IdentityTracking = RPC_C_QOS_IDENTITY_DYNAMIC;
// The client system service needs to impersonate the security context
// of the client utility on the remote systems where it may replicate
// files from.
// We need to impersonate with level delegate, since the server system
// service will need to impersonate the user further.
SecurityQOS.ImpersonationType = RPC_C_IMP_LEVEL_DELEGATE;
// Generate the principal name for the local system service.
// We can't just use the a string constant, because then
// we rely on a particular English-language representation of the
// principal.
if (AllocateAndInitializeSid(&SIDAuth, 1,
SECURITY_NETWORK_SERVICE_RID,
0, 0, 0, 0, 0, 0, 0,
&pSID) == 0) {
printf_s("AllocateAndInitializeSid failed\n");
exit(EXIT_FAILURE);
}
if (LookupAccountSid(NULL, // name of local or remote computer
pSID, // security identifier
Name, // account name buffer
&cbName, // size of account name buffer
DomainName, // domain name
&cbDomainName, // size of domain name buffer
&Use) == 0) { // SID type
printf_s("LookupAccountSid failed\n");
exit(EXIT_FAILURE);
}
if(bDebug) _tprintf_s(TEXT("Server \"magic name\" is %s\n"), Name);
pszServerPrincipalName = (RPC_STR)Name;
// Set authentication and authorisation information for
// the binding handle.
// By speciying NULL for the 5th parameter we use the security login
// context for the current address space.
// The security level is "PRIVACY" since it is the only level
// provided by LRPC.
// We are assured of talking to a local service running with
// system privileges.
rpcstatus = RpcBindingSetAuthInfoEx(hFileRepClient, pszServerPrincipalName, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_AUTHN_WINNT, NULL, NULL, &SecurityQOS);
RPC_EEINFO_EXIT_IF_FAIL(rpcstatus, TEXT("RpcBindingSetAuthInfo"));
#endif
// Make the RPC to request file replication.
RpcTryExcept {
if(bDebug) _tprintf_s(TEXT("Calling the remote procedure RequestFile(hFileRepClient, %s, %s, %s)\n"), ServerName, RemoteFileName, LocalFileName);
RequestFile(hFileRepClient, ServerName, RemoteFileName, LocalFileName);
if(bDebug) _tprintf_s(TEXT("RequestFile() finished\n"));
}
// Return "non-fatal" errors. Catching fatal errors
// makes it harder to debug.
RpcExcept ( ( (RpcExceptionCode() != STATUS_ACCESS_VIOLATION) &&
(RpcExceptionCode() != STATUS_DATATYPE_MISALIGNMENT) &&
(RpcExceptionCode() != STATUS_PRIVILEGED_INSTRUCTION) &&
(RpcExceptionCode() != STATUS_ILLEGAL_INSTRUCTION) &&
(RpcExceptionCode() != STATUS_BREAKPOINT))
? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
PrintProcFailureEEInfo(TEXT("RequestFile"), RpcExceptionCode());
exit(EXIT_FAILURE);
}
RpcEndExcept;
// Free the binding.
rpcstatus = RpcBindingFree((VOID **)&hFileRepClient);
ASSERT(rpcstatus == RPC_S_OK);
if(bDebug) _tprintf_s(TEXT("Success!\n"));
return(EXIT_SUCCESS);
} // end main()
/*
MIDL allocate() and free()
*/
VOID __RPC_FAR * __RPC_API midl_user_allocate(size_t len) {
return(AutoHeapAlloc(len));
}
VOID __RPC_API midl_user_free(VOID __RPC_FAR * ptr) {
if(ptr != NULL) {
AutoHeapFree(ptr);
}
}
// end FileRep.cpp