|
|
//============================================================================
// Copyright (c) 1996, Microsoft Corporation
//
// File: script.c
//
// History:
// Abolade-Gbadegesin 03-29-96 Created.
//
// This file contains functions implementing the NT port
// of Win9x dial-up scripting, listed in alphabetical order.
//
// See scriptp.h for details on the NT implementation.
//============================================================================
#include <scriptp.h>
#include <lmwksta.h> // For NetWkstaUserGetInfo
#include <lmapibuf.h> // For NetApiBufferFree
//
// Handle of module-instance for this DLL
//
HANDLE g_hinst; //
// global critical section used to synhronize access to IP address strings
//
CRITICAL_SECTION g_cs; //
// name of file to which script syntax errors are logged
//
CHAR c_szScriptLog[] = RASSCRIPT_LOG; //
// event handle which would be notified in case of IPAddress Change
//
HANDLE hIpAddressSet = INVALID_HANDLE_VALUE;
#define NET_SVCS_GROUP "-k netsvcs"
//----------------------------------------------------------------------------
// Function: DLLMAIN
//
// DLL entry-point for RASSCRIPT
//----------------------------------------------------------------------------
BOOL WINAPI RasScriptDllMain( IN HINSTANCE hinstance, IN DWORD dwReason, IN PVOID pUnused ) {
BOOL bRetVal = TRUE;
if (dwReason == DLL_PROCESS_ATTACH) {
g_hinst = (HANDLE)hinstance;
try { InitializeCriticalSection(&g_cs); } except (EXCEPTION_EXECUTE_HANDLER) { bRetVal = FALSE; } } else if (dwReason == DLL_PROCESS_DETACH) {
DeleteCriticalSection(&g_cs); }
return bRetVal; }
//----------------------------------------------------------------------------
// Function: RasScriptExecute
//
// Examines the given connection, and if there is a script for the connection,
// executes the script to completion.
// Returns the error code from script processing if a script is given,
// and returns NO_ERROR otherwise.
//----------------------------------------------------------------------------
DWORD APIENTRY RasScriptExecute( IN HRASCONN hrasconn, IN PBENTRY* pEntry, IN CHAR* pszUserName, IN CHAR* pszPassword, OUT CHAR* pszIpAddress ) {
DWORD dwErr; HANDLE hevent = NULL, hscript = NULL; HANDLE hEvents[2];
RASSCRPT_TRACE("RasScriptExecute");
do { //
// create event on which to receive notification
//
hevent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!hevent) { RASSCRPT_TRACE1("error %d creating event", dwErr = GetLastError()); break; }
// Create a separate event for SCRIPTCODE_IpAddressSet
// event. We hit a timing window ow where we lose this
// event (when we get a script complete event immediately
// after a SCRIPTCODE_IpAddressSet event. bug 75226.
hIpAddressSet = CreateEvent (NULL, FALSE, FALSE, NULL);
if (!hIpAddressSet) {
RASSCRPT_TRACE1("error %d creating event", dwErr = GetLastError()); break;
}
//
// initialize script processing
//
dwErr = RasScriptInit( hrasconn, pEntry, pszUserName, pszPassword, 0, hevent, &hscript );
if (dwErr != NO_ERROR) { RASSCRPT_TRACE1("error %d initializing scripting", dwErr); break; }
hEvents[0] = hevent; hEvents[1] = hIpAddressSet;
//
// loop waiting for script to finish running
//
for ( ; ; ) {
dwErr = WaitForMultipleObjects(2, hEvents, FALSE, INFINITE);
if (dwErr - WAIT_OBJECT_0 == 0) {
//
// Retrieve the code for the event which occurred
//
DWORD dwCode = RasScriptGetEventCode(hscript);
RASSCRPT_TRACE1("RasScriptExecute: eventcode %d", dwCode);
//
// Handle the event
//
if (dwCode == SCRIPTCODE_Done || dwCode == SCRIPTCODE_Halted || dwCode == SCRIPTCODE_HaltedOnError) {
RASSCRPT_TRACE("script processing completed");
dwErr = NO_ERROR;
break; } }
else if (dwErr - WAIT_OBJECT_0 == 1) {
//
// The IP address has been changed;
// read the new IP address into the caller's buffer
//
RASSCRPT_TRACE("IP address changed");
dwErr = RasScriptGetIpAddress(hscript, pszIpAddress); RASSCRPT_TRACE2("RasScriptGetIpAddress(e=%d,a=%s)",dwErr,pszIpAddress); } }
} while(FALSE);
if (hscript) { RasScriptTerm(hscript); }
if (hevent) { CloseHandle(hevent); }
if (hIpAddressSet) { CloseHandle (hIpAddressSet); }
return dwErr; }
//----------------------------------------------------------------------------
// Function: RasScriptGetEventCode
//
// This function should be called to retrieve the event-code
// when the scripting thread signals an event.
// The event codes which may be returned are as follows:
//
// NO_ERROR: no code has been set
// SCRIPTCODE_Done: the script has finished running;
// the thread blocks until RasScriptTerm is called.
// SCRIPTCODE_InputNotify: data is available in the buffer; if the buffer
// is full, the thread blocks until
// RasScriptReceive is called and the data
// is read successfully.
// SCRIPTCODE_KeyboardEnable: the keyboard should be enabled.
// SCRIPTCODE_KeyboardDisable: the keyboard should be disabled.
// SCRIPTCODE_IpAddressSet: the IP address has changed; the new address
// can be retrieved via RasScriptGetIPAddress.
// SCRIPTCODE_HaltedOnError: the script has halted due to an error.
//----------------------------------------------------------------------------
DWORD RasScriptGetEventCode( IN HANDLE hscript ) {
SCRIPTCB* pscript = (SCRIPTCB *)hscript;
RASSCRPT_TRACE("RasGetEventCode");
if (!pscript) { return ERROR_INVALID_PARAMETER; }
return pscript->dwEventCode; }
//----------------------------------------------------------------------------
// Function: RasScriptGetIpAddress
//
// This function retrieves the current IP address as set by the script.
//----------------------------------------------------------------------------
DWORD RasScriptGetIpAddress( IN HANDLE hscript, OUT CHAR* pszIpAddress ) {
SCRIPTCB* pscript = (SCRIPTCB *)hscript;
RASSCRPT_TRACE("RasGetIpAddress");
if (!pscript || !pszIpAddress) { return ERROR_INVALID_PARAMETER; }
//
// Access to the IP address string must be synchronized
// since it may also be accessed via RxSetIPAddress
//
EnterCriticalSection(&g_cs);
if (pscript->pszIpAddress) {
lstrcpy(pszIpAddress, pscript->pszIpAddress); } else {
lstrcpy(pszIpAddress, "0.0.0.0"); }
LeaveCriticalSection(&g_cs);
return NO_ERROR; }
//----------------------------------------------------------------------------
// Function: RasScriptInit
//
// Initializes for script processing on the given HRASCONN.
//
// This function creates a thread which handles script input and output
// on the given connection's port.
//
// If there is no script for the connection, this function returns an error
// unless the flag RASSCRIPT_NotifyOnInput is specified, in which case
// the thread loops posting receive-data requests on the connection's port
// until RasScriptTerm is called.
//
// If there is a script for the connection, the thread runs the script
// to completion. If the flag RASSCRIPT_NotifyOnInput is specified,
// the caller is notified when data is received on the port. The caller
// can then retrieve the data by calling RasScriptReceive.
//
// Notification may be event-based or message-based. By default, notification
// is event-based, and "Hnotifier" is treated as an event-handle.
// The event is signalled to by the scripting thread, and the caller retrieves
// the event code by calling RasScriptGetEventCode.
//
// Setting the flag RASSCRIPT_HwndNotify selects message-based notification,
// and indicates that "Hnotifier" is an HWND. The WM_RASSCRIPT event is sent
// to the window by the scripting thread, and "LParam" in the message sent
// contains the event code. See RasScriptGetEventCode for descriptions
// of the codes sent by the scripting thread.
//----------------------------------------------------------------------------
DWORD APIENTRY RasScriptInit( IN HRASCONN hrasconn, IN PBENTRY* pEntry, IN CHAR* pszUserName, IN CHAR* pszPassword, IN DWORD dwFlags, IN HANDLE hNotifier, OUT HANDLE* phscript ) {
DWORD dwErr, dwSyntaxError = NO_ERROR; static const CHAR szSwitch[] = MXS_SWITCH_TXT; SCRIPTCB* pscript = NULL; #ifdef UNICODEUI
//
// Define structures to use depending on whether or not the RAS UI
// is being built with Unicode.
//
#define PUISTR CHAR*
#define PUIRCS RASCONNSTATUSA*
#define PUIRC RASCREDENTIALSA*
RASCONNSTATUSW rcs; WCHAR* pszSwitch = StrDupWFromA(MXS_SWITCH_TXT); #else
#define PUISTR CHAR*
#define PUIRCS RASCONNSTATUSA*
#define PUIRC RASCREDENTIALSA*
RASCONNSTATUSA rcs; CHAR* pszSwitch = szSwitch; #endif
RASSCRPT_TRACE_INIT("RASSCRPT"); RASSCRPT_TRACE("RasScriptInit");
//
// validate arguments
//
if (phscript) { *phscript = NULL; }
if (!hrasconn || !pEntry || !pszUserName || !pszPassword || !hNotifier || !phscript) {
RASSCRPT_TRACE("RasScriptInit: required parameter not specified");
#ifdef UNICODEUI
Free(pszSwitch); #endif
return ERROR_INVALID_PARAMETER; }
//
// initialize script processing
//
do {
DWORD dwsize; DWORD dwthread; HANDLE hthread;
//
// Load required DLL function pointers.
//
dwErr = LoadRasapi32Dll(); if (dwErr) break; dwErr = LoadRasmanDll(); if (dwErr) break; //
// Initialize RAS
//
dwErr = g_pRasInitialize();
if ( dwErr ) break;
/*
//
// Connect to the local rasman server
//
dwErr = g_pRasRpcConnect ( NULL, NULL );
if (dwErr) break; */
//
// allocate space for a control block
//
pscript = Malloc(sizeof(*pscript));
if (!pscript) { dwErr = GetLastError(); RASSCRPT_TRACE2("error %d allocating %d bytes", dwErr, sizeof(*pscript)); break; }
//
// initialize the control block
//
ZeroMemory(pscript, sizeof(*pscript));
//
// copy the argument fields
//
pscript->hrasconn = hrasconn; pscript->pEntry = pEntry; pscript->dwFlags = dwFlags; pscript->hNotifier = hNotifier; pscript->hport = g_pRasGetHport(hrasconn);
if (pscript->pEntry->pszIpAddress) {
//
// Copy the IP address for the entry
//
pscript->pszIpAddress = Malloc(lstrlenUI(pscript->pEntry->pszIpAddress) + 1);
if (pscript->pszIpAddress) {
StrCpyAFromUI( pscript->pszIpAddress, pscript->pEntry->pszIpAddress ); } else {
RASSCRPT_TRACE("error copying entry's IP address");
dwErr = ERROR_NOT_ENOUGH_MEMORY;
break; } }
//
// Initialize our Win9x-compatible session-config-info structure
//
ZeroMemory(&pscript->sci, sizeof(pscript->sci));
pscript->sci.dwSize = sizeof(pscript->sci); StrCpyAFromUI(pscript->sci.szEntryName, pEntry->pszEntryName); lstrcpy(pscript->sci.szUserName, pszUserName); lstrcpy(pscript->sci.szPassword, pszPassword);
//
// See if the user name is missing;
// if so, read the currently-logged on user's name
//
if (!pscript->sci.szUserName[0]) {
WKSTA_USER_INFO_1* pwkui1 = NULL;
//
// Not all params were specified, so read the dial-params
// for this phonebook entry
//
dwErr = NetWkstaUserGetInfo(NULL, 1, (LPBYTE*)&pwkui1); RASSCRPT_TRACE2("NetWkstaUserGetInfo(e=%d,u=(%ls))", dwErr, (pwkui1) ? pwkui1->wkui1_username : L"null");
if (dwErr == NO_ERROR && pwkui1 != NULL) {
StrCpyAFromUI(pscript->sci.szUserName, (LPCWSTR)pwkui1->wkui1_username);
NetApiBufferFree(pwkui1); } }
//
// See if there is a script for this connection's state;
// if there is one then the device-type will be "switch"
// and the device-name will be the script path
//
ZeroMemory(&rcs, sizeof(rcs));
rcs.dwSize = sizeof(rcs);
dwErr = g_pRasGetConnectStatus(hrasconn, (PUIRCS)&rcs);
if (dwErr != NO_ERROR) { RASSCRPT_TRACE1("error %d getting connect status", dwErr); break; }
//
// Check the device-type (will be "switch" for scripted entries)
// and the device name (will be a filename for scripted entries)
//
if (lstrcmpiUI(rcs.szDeviceType, pszSwitch) == 0 && GetFileAttributesUI(rcs.szDeviceName) != 0xFFFFFFFF) {
CHAR szDevice[RAS_MaxDeviceName + 1], *pszDevice = szDevice;
StrCpyAFromUI(szDevice, rcs.szDeviceName);
//
// The device-type is "Switch" and the device-name
// contains the name of an existing file;
// initialize the SCRIPTDATA structure.
//
dwErr = RsInitData(pscript, pszDevice);
//
// If there was a syntax error in the script, we continue
// with the initialization, but record the error code.
// on any other error, we immediately terminate initialization.
//
if (dwErr == ERROR_SCRIPT_SYNTAX) { dwSyntaxError = dwErr; } else if (dwErr != NO_ERROR) { break; } }
//
// Initialize RASMAN fields, allocating buffers for RASMAN I/O
//
dwsize = SIZE_RecvBuffer; dwErr = g_pRasGetBuffer(&pscript->pRecvBuffer, &dwsize); RASSCRPT_TRACE2("RasGetBuffer:e=%d,s=%d", dwErr, dwsize);
if (dwErr != NO_ERROR) { RASSCRPT_TRACE1("error %d allocating receive-buffer", dwErr); break; }
dwsize = SIZE_SendBuffer; dwErr = g_pRasGetBuffer(&pscript->pSendBuffer, &dwsize); RASSCRPT_TRACE2("RasGetBuffer:e=%d,s=%d", dwErr, dwsize);
if (dwErr != NO_ERROR) { RASSCRPT_TRACE1("error %d alloacting send-buffer", dwErr); break; }
//
// Create synchronization events used to control the background thread
//
pscript->hRecvRequest = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!pscript->hRecvRequest) { RASSCRPT_TRACE1("error %d creating receive-event", dwErr = GetLastError()); break; }
pscript->hRecvComplete = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!pscript->hRecvComplete) { RASSCRPT_TRACE1("error %d creating received-event", dwErr = GetLastError()); break; }
pscript->hStopRequest = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!pscript->hStopRequest) { RASSCRPT_TRACE1("error %d creating stop-event", dwErr = GetLastError()); break; }
pscript->hStopComplete = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!pscript->hStopComplete) { RASSCRPT_TRACE1("error %d creating stopped-event", dwErr = GetLastError()); break; }
//
// Create the thread which will receive data and process the script
//
hthread = CreateThread( NULL, 0, RsThread, (PVOID)pscript, 0, &dwthread );
if (!hthread) { RASSCRPT_TRACE1("error %d creating script-thread", dwErr = GetLastError()); break; }
CloseHandle(hthread);
pscript->dwFlags |= RASSCRIPT_ThreadCreated;
if ((VOID*)pszSwitch != (VOID*)szSwitch) { Free0(pszSwitch); }
//
// we've successfully initialized, return control to caller
//
*phscript = (HANDLE)pscript;
//
// if there was a syntax error in the script, return the special
// error code (ERROR_SCRIPT_SYNTAX) to indicate the problem;
// otherwise return NO_ERROR.
//
return (dwSyntaxError ? dwSyntaxError : NO_ERROR);
} while(FALSE);
//
// an error occurred, so do cleanup
//
if ((VOID*)pszSwitch != (VOID*)szSwitch) { Free0(pszSwitch); }
RasScriptTerm((HANDLE)pscript);
return (dwErr ? dwErr : ERROR_UNKNOWN); }
//----------------------------------------------------------------------------
// Function: RasScriptReceive
//
// Called to retrieve the contents of the scripting thread's input buffer.
// When this function completes successfully, if the input buffer was full
// and the scripting thread was blocked, the thread continues executing.
//
// On input, "PdwBufferSize" should contain the size of "PBuffer", unless
// "PBuffer" is NULL, in which case "*PdwBufferSize" is treated as 0.
// On output, "PdwBufferSize" contains the size required to read
// the input buffer, and if the return value is NO_ERROR, then "PBuffer"
// contains the data in the input buffer. If the return value is
// ERROR_INSUFFICIENT_BUFFER, "PBuffer" was not large enough.
//----------------------------------------------------------------------------
DWORD APIENTRY RasScriptReceive( IN HANDLE hscript, IN BYTE* pBuffer, IN OUT DWORD* pdwBufferSize ) {
SCRIPTCB* pscript = (SCRIPTCB *)hscript;
RASSCRPT_TRACE("RasScriptReceive");
//
// return if the caller didn't request input-notification
// or if no buffer-size is available
//
if (!pscript || !pdwBufferSize || !(pscript->dwFlags & RASSCRIPT_NotifyOnInput)) { return ERROR_INVALID_PARAMETER; }
//
// return if no buffer or if buffer too small
//
if (!pBuffer || *pdwBufferSize < pscript->dwRecvSize) { *pdwBufferSize = pscript->dwRecvSize; return ERROR_INSUFFICIENT_BUFFER; }
//
// copy the data, and notify the thread that the data has been read
//
CopyMemory(pBuffer, pscript->pRecvBuffer, pscript->dwRecvSize);
*pdwBufferSize = pscript->dwRecvSize;
SetEvent(pscript->hRecvComplete);
return NO_ERROR; }
//----------------------------------------------------------------------------
// Function: RasScriptSend
//
// This function transmits bytes over the connection's port.
//
// "DwBufferSize" contains the number of bytes to insert from "PBuffer"
//----------------------------------------------------------------------------
DWORD APIENTRY RasScriptSend( IN HANDLE hscript, IN BYTE* pBuffer, IN DWORD dwBufferSize ) {
DWORD dwsize; DWORD dwErr; SCRIPTCB *pscript = (SCRIPTCB *)hscript;
RASSCRPT_TRACE("RasScriptSend");
if (!pscript || !pBuffer || !dwBufferSize) { return ERROR_INVALID_PARAMETER; }
//
// send all the data in the buffer
//
for (dwsize = min(dwBufferSize, SIZE_SendBuffer); dwBufferSize; dwBufferSize -= dwsize, pBuffer += dwsize, dwsize = min(dwBufferSize, SIZE_SendBuffer)) {
CopyMemory(pscript->pSendBuffer, pBuffer, dwsize);
dwErr = g_pRasPortSend( pscript->hport, pscript->pSendBuffer, dwsize ); RASSCRPT_TRACE1("g_pRasPortSend=%d", dwErr); DUMPB(pBuffer, dwsize); }
return NO_ERROR; }
//----------------------------------------------------------------------------
// Function: RasScriptTerm
//
// This function terminates script processing, stopping the scripting thread.
// The return code is the code from processing the script, and it may be
//
// NO_ERROR: the script had finished running, or the connection
// had no script and the scripting thread was acting
// in simple I/O mode.
// ERROR_MORE_DATA: the script was still running.
//----------------------------------------------------------------------------
DWORD APIENTRY RasScriptTerm( IN HANDLE hscript ) {
SCRIPTCB* pscript = hscript;
RASSCRPT_TRACE("RasScriptTerm");
if (!pscript) { return ERROR_INVALID_PARAMETER; }
//
// stop the thread if it is running
//
if (pscript->dwFlags & RASSCRIPT_ThreadCreated) {
SetEvent(pscript->hStopRequest);
WaitForSingleObject(pscript->hStopComplete, INFINITE); }
if (pscript->pdata) { RsDestroyData(pscript); }
if (pscript->hStopRequest) { CloseHandle(pscript->hStopRequest); }
if (pscript->hStopComplete) { CloseHandle(pscript->hStopComplete); }
if (pscript->hRecvRequest) { CloseHandle(pscript->hRecvRequest); }
if (pscript->hRecvComplete) { CloseHandle(pscript->hRecvComplete); }
if (pscript->pRecvBuffer) { g_pRasFreeBuffer(pscript->pRecvBuffer); }
if (pscript->pSendBuffer) { g_pRasFreeBuffer(pscript->pSendBuffer); }
Free0(pscript->pszIpAddress);
Free(pscript);
RASSCRPT_TRACE_TERM();
return NO_ERROR; }
//----------------------------------------------------------------------------
// Function: RsDestroyData
//
// This function destroys the SCRIPTDATA portion of a SCRIPTCB.
//----------------------------------------------------------------------------
DWORD RsDestroyData( IN SCRIPTCB* pscript ) {
SCRIPTDATA* pdata = pscript->pdata;
if (!pdata) { return ERROR_INVALID_PARAMETER; }
if (pdata->pmoduledecl) { Decl_Delete((PDECL)pdata->pmoduledecl); }
if (pdata->pastexec) { Astexec_Destroy(pdata->pastexec); Free(pdata->pastexec); }
if (pdata->pscanner) { Scanner_Destroy(pdata->pscanner); }
return NO_ERROR; }
//----------------------------------------------------------------------------
// Function: RsInitData
//
// This function initializes the SCRIPTDATA portion of a SCRIPTCB,
// preparing for script-processing.
//----------------------------------------------------------------------------
DWORD RsInitData( IN SCRIPTCB* pscript, IN LPCSTR pszScriptPath ) {
RES res; DWORD dwErr = ERROR_SUCCESS; SCRIPTDATA *pdata;
RASSCRPT_TRACE("RsInitData");
do {
//
// allocate space for the SCRIPTDATA;
//
pscript->pdata = pdata = Malloc(sizeof(*pdata));
if (!pdata) { RASSCRPT_TRACE1("error %d allocating SCRIPTDATA", dwErr = GetLastError()); break; }
//
// initialize the structure
//
ZeroMemory(pdata, sizeof(*pdata));
pdata->hscript = (HANDLE)pscript; lstrcpy(pdata->script.szPath, pszScriptPath);
//
// create a scanner and use it to open the script
//
res = Scanner_Create(&pdata->pscanner, &pscript->sci);
if (RFAILED(res)) { RASSCRPT_TRACE1("failure %d creating scanner", res); break; }
res = Scanner_OpenScript(pdata->pscanner, pszScriptPath);
if (res == RES_E_FAIL || RFAILED(res)) { RASSCRPT_TRACE1("failure %d opening script", res); break; }
//
// allocate a script-execution handler
//
pdata->pastexec = Malloc(sizeof(*pdata->pastexec));
if (!pdata->pastexec) { RASSCRPT_TRACE1("error %d allocating ASTEXEC", dwErr = GetLastError()); break; }
ZeroMemory(pdata->pastexec, sizeof(*pdata->pastexec));
//
// initialize the script-execution handler
//
res = Astexec_Init( pdata->pastexec, pscript, &pscript->sci, Scanner_GetStxerrHandle(pdata->pscanner) );
if (!RSUCCEEDED(res)) { RASSCRPT_TRACE1("failure %d initializing ASTEXEC", res); break; }
Astexec_SetHwnd(pdata->pastexec, (HWND)pdata);
//
// parse the script using the created scanner
// and writing into the execution-handler's symbol-table
//
res = ModuleDecl_Parse( &pdata->pmoduledecl, pdata->pscanner, pdata->pastexec->pstSystem );
if (RSUCCEEDED(res)) {
//
// generate code for the script
//
res = ModuleDecl_Codegen(pdata->pmoduledecl, pdata->pastexec); }
//
// see if anything went wrong
//
if (RFAILED(res)) {
//
// there was an error parsing the script.
// we return the special error code ERROR_SCRIPT_SYNTAX
// and log the errors to a file.
//
// This is not necessarily a fatal error, and so returning
// the above error doesn't cause script-initialization to fail,
// since if the user is in interactive mode, the connection
// may be completed manually by typing into the terminal window.
//
// If we are not in interactive mode, this is a fatal error,
// and RasScriptExecute handles the condition correctly
// by terminating the script immediately
//
RASSCRPT_TRACE1("failure %d parsing script", res);
RxLogErrors( (HANDLE)pscript, (VOID*)Scanner_GetStxerrHandle(pdata->pscanner) );
Decl_Delete((PDECL)pdata->pmoduledecl); Astexec_Destroy(pdata->pastexec); Free(pdata->pastexec); Scanner_Destroy(pdata->pscanner);
pscript->pdata = NULL;
dwErr = ERROR_SCRIPT_SYNTAX;
return dwErr; }
//
// all went well, return
//
return NO_ERROR;
} while(FALSE);
//
// an error occurred, so do cleanup
//
if (pscript->pdata) { RsDestroyData(pscript); }
return (dwErr ? dwErr : ERROR_UNKNOWN); }
//----------------------------------------------------------------------------
// Function: RsPostReceive
//
// Internal function:
// posts receive-request to RASMAN
//----------------------------------------------------------------------------
DWORD RsPostReceive( IN SCRIPTCB* pscript ) {
DWORD dwSize; DWORD dwErr;
RASSCRPT_TRACE("RsPostReceive");
dwSize = SIZE_RecvBuffer;
dwErr = g_pRasPortReceive( pscript->hport, pscript->pRecvBuffer, &dwSize, SECS_RecvTimeout, pscript->hRecvRequest );
RASSCRPT_TRACE2("RsPostReceive=%d,%d", dwErr, dwSize);
return dwErr; }
BOOL IsRasmanProcess() { CHAR *pszCmdLine = NULL; BOOL fRet = FALSE;
pszCmdLine = GetCommandLineA();
if( (NULL != pszCmdLine) && (strstr(pszCmdLine, NET_SVCS_GROUP))) { fRet = TRUE; }
return fRet; }
DWORD RsPostReceiveEx( IN SCRIPTCB* pscript ) {
DWORD dwSize = 0; DWORD dwErr = ERROR_SUCCESS;
RASSCRPT_TRACE("RsPostReceiveEx");
if(IsRasmanProcess()) { goto done; }
RASSCRPT_TRACE("Calling RsPostReceiveEx");
dwSize = SIZE_RecvBuffer; dwErr = g_pRasPortReceiveEx( pscript->hport, pscript->pRecvBuffer, &dwSize );
done:
RASSCRPT_TRACE2("RsPostReceiveEx=%d, %d",dwErr, dwSize );
return dwErr;
}
//----------------------------------------------------------------------------
// Function: RsSignal
//
// Internal function:
// this is called to signal the notifier for a script, which may involve
// setting an event or sending a message.
//----------------------------------------------------------------------------
VOID RsSignal( IN SCRIPTCB* pscript, IN DWORD dwEventCode ) {
RASSCRPT_TRACE1("RsSignal: %d", dwEventCode);
InterlockedExchange(&pscript->dwEventCode, dwEventCode);
if (pscript->dwFlags & RASSCRIPT_HwndNotify) {
SendNotifyMessage( (HWND)pscript->hNotifier, WM_RASAPICOMPLETE, 0, dwEventCode ); } else {
SetEvent(pscript->hNotifier); } }
//----------------------------------------------------------------------------
// Function: RsThread
//
// This function is the entry-point for the script processing thread.
//
// The scripting thread operates in a loop, posting receive requests
// and receiving incoming data. If a script is associated with the port,
// the thread also runs the script.
//----------------------------------------------------------------------------
DWORD RsThread( IN PVOID pParam ) {
WORD wSize; #define POS_STOP 0
#define POS_RECV 1
#define POS_LAST 2
BOOL bFirstRecv = TRUE; HANDLE hEvents[POS_LAST]; SCRIPTCB* pscript = (SCRIPTCB *)pParam; SCRIPTDATA* pdata = pscript->pdata; DWORD dwErr, dwTicksBefore, dwTicksAfter, dwTicksElapsed;
RASSCRPT_TRACE("RsThread");
//
// post receive-request to RASMAN
//
dwErr = RsPostReceive(pscript); if (dwErr != NO_ERROR && dwErr != PENDING) {
RASSCRPT_TRACE1("error %d posting receive to RASMAN", dwErr);
RsPostReceiveEx ( pscript );
RsSignal(pscript, SCRIPTCODE_Halted);
SetEvent(pscript->hStopComplete);
return dwErr; }
//
// set up event array; we place the stop-request event first
// in the array since the receive-event will be signalled more often
// and placing it first might result in starvation
// (waits are always satisfied by the first signalled object)
//
hEvents[POS_STOP] = pscript->hStopRequest; hEvents[POS_RECV] = pscript->hRecvRequest;
if (pdata) { pdata->dwTimeout = INFINITE; }
while (TRUE) {
//
// wait for receive to complete, for stop signal,
// or for timeout to expire
//
// save the tick count so we can tell how long the wait lasted
//
dwTicksBefore = GetTickCount();
dwErr = WaitForMultipleObjects( POS_LAST, hEvents, FALSE, pdata ? pdata->dwTimeout:INFINITE );
dwTicksAfter = GetTickCount();
//
// see if the tick count wrapped around, and if so
// adjust so we always get the correct elapsed time
// from the expression (dwTicksAfter - dwTicksBefore)
//
if (dwTicksAfter < dwTicksBefore) { dwTicksAfter += MAXDWORD - dwTicksBefore; dwTicksBefore = 0; }
dwTicksElapsed = dwTicksAfter - dwTicksBefore;
RASSCRPT_TRACE1("RsThread: waited for %d milliseconds", dwTicksElapsed);
//
// if the timeout isn't INFINITE, decrement it by
// the amount of time we've already waited
//
if (pdata && pdata->dwTimeout != INFINITE) {
if (dwTicksElapsed >= pdata->dwTimeout) { pdata->dwTimeout = INFINITE; } else { pdata->dwTimeout -= dwTicksElapsed; } }
//
// Handle the return-code from WaitForMultipleObjects
//
if (dwErr == (WAIT_OBJECT_0 + POS_STOP)) {
//
// stop-request signalled, break
//
RASSCRPT_TRACE("RsThread: stop event signalled");
RsSignal(pscript, SCRIPTCODE_Halted);
break; } else if (dwErr == WAIT_TIMEOUT) {
if (!pdata) { continue; }
//
// wait timed out, so that means we were blocked
// on a "delay" or "waitfor ... until" statement;
//
Astexec_ClearPause(pdata->pastexec);
//
// if we blocked because of a "waitfor ... until",
// finish processing the statement
//
if (Astexec_IsWaitUntil(pdata->pastexec)) {
Astexec_SetStopWaiting(pdata->pastexec);
Astexec_ClearWaitUntil(pdata->pastexec); }
//
// continue processing the script
//
if (RsThreadProcess(pscript) == ERROR_NO_MORE_ITEMS) {
//
// the script has stopped; if done, break;
// otherwise, continue receiving data
//
if (pscript->dwEventCode == SCRIPTCODE_Done) {
break; } else {
//
// Cleanup the script, but continue receiving data
//
RsDestroyData(pscript);
pdata = pscript->pdata = NULL; } } } else if (dwErr == (WAIT_OBJECT_0 + POS_RECV)) {
//
// receive completed
//
RASMAN_INFO info; DWORD dwStart, dwRead;
RASSCRPT_TRACE("RsThread: receive event signalled");
//
// Get the data received
//
dwErr = RsPostReceiveEx ( pscript );
if ( NO_ERROR != dwErr && PENDING != dwErr ) { RASSCRPT_TRACE1("error %d in RsPostReceiveEx", dwErr);
RsSignal(pscript, SCRIPTCODE_Halted );
break; }
//
// get the number of bytes received
//
dwErr = g_pRasGetInfo(NULL, pscript->hport, &info);
if (dwErr != NO_ERROR) {
RASSCRPT_TRACE1("error %d retrieving RASMAN_INFO", dwErr);
RsSignal(pscript, SCRIPTCODE_Halted);
break; }
if( (info.RI_LastError != NO_ERROR) && (info.RI_ConnState != CONNECTING)) { RASSCRPT_TRACE("Link dropped! port no longer in connecting state");
RsSignal(pscript, SCRIPTCODE_Halted);
break; } if (info.RI_LastError != NO_ERROR) { RASSCRPT_TRACE1("last error: %d", info.RI_LastError); continue; }
RASSCRPT_TRACE1("RsThread: received %d bytes", info.RI_BytesReceived);
//
// on the first receive, we proceed even if there aren't any
// characters read, since we need to run the first script commands
//
if (!bFirstRecv && info.RI_BytesReceived == 0) {
//
// something went wrong, post another receive request
//
dwErr = RsPostReceive(pscript);
if ( dwErr != NO_ERROR && dwErr != PENDING) { RASSCRPT_TRACE1("error %d in RsPostReceive", dwErr);
RsSignal(pscript, SCRIPTCODE_Halted);
break; }
continue; }
bFirstRecv = FALSE;
pscript->dwRecvSize = info.RI_BytesReceived; pscript->dwRecvRead = 0;
DUMPB(pscript->pRecvBuffer, pscript->dwRecvSize);
//
// if the creator wants to know when data arrives,
// signal the creator's notification now;
// wait till the creator reads the data before proceeding
//
if (info.RI_BytesReceived && (pscript->dwFlags & RASSCRIPT_NotifyOnInput)) {
RsSignal(pscript, SCRIPTCODE_InputNotify);
WaitForSingleObject(pscript->hRecvComplete, INFINITE); }
//
// if we have no script that's all we have to do,
// so just post another receive request and go back to waiting
//
if (!pdata) {
dwErr = RsPostReceive(pscript);
if ( dwErr != NO_ERROR && dwErr != PENDING ) { RASSCRPT_TRACE1("error %d in RsPostReceive",dwErr);
RsSignal(pscript, SCRIPTCODE_Halted);
break; }
continue; }
//
// read the data into the script's circular buffer
//
ReadIntoBuffer(pdata, &dwStart, &dwRead);
//
// do more script processing
//
if (RsThreadProcess(pscript) == ERROR_NO_MORE_ITEMS) {
//
// the script has stopped; if done, break;
// otherwise, continue receiving data
//
if (pscript->dwEventCode == SCRIPTCODE_Done) {
break; } else {
//
// Cleanup the script, but continue receiving data
//
RsDestroyData(pscript);
pdata = pscript->pdata = NULL; } } } }
//
// cancel any pending receives
//
g_pRasPortCancelReceive(pscript->hport);
SetEvent(pscript->hStopComplete);
RASSCRPT_TRACE("RsThread done");
return NO_ERROR; }
//----------------------------------------------------------------------------
// Function: RsThreadProcess
//
// Called to process the script until it is blocked
// by a "waitfor" statement or a "delay" statement.
//----------------------------------------------------------------------------
DWORD RsThreadProcess( IN SCRIPTCB* pscript ) {
RES res; DWORD dwErr; SCRIPTDATA *pdata = pscript->pdata;
RASSCRPT_TRACE("RsThreadProcess");
//
// now step through the script until we are blocked
// by a "delay" statement or a "waitfor" statement
//
dwErr = NO_ERROR;
do {
//
// break if its time to stop
//
if (WaitForSingleObject(pscript->hStopRequest, 0) == WAIT_OBJECT_0) {
SetEvent(pscript->hStopRequest);
break; }
//
// process next command
//
res = Astexec_Next(pdata->pastexec);
// if (res != RES_OK) { break; }
//
// examine the resulting state
//
if (Astexec_IsDone(pdata->pastexec) || Astexec_IsHalted(pdata->pastexec)) {
//
// the script has come to an end, so set our stop event
// and break out of this loop
//
RASSCRPT_TRACE("RsThreadProcess: script completed");
//
// do stop-completion notification
//
if (Astexec_IsDone(pdata->pastexec)) { RsSignal(pscript, SCRIPTCODE_Done); } else if (!RFAILED(res)) { RsSignal(pscript, SCRIPTCODE_Halted); } else { RsSignal(pscript, SCRIPTCODE_HaltedOnError); }
dwErr = ERROR_NO_MORE_ITEMS;
break; } else if (Astexec_IsReadPending(pdata->pastexec)) {
//
// we're blocked waiting for input,
// so post another receive request and go back
// to waiting for data;
// if we're blocked on a "waitfor ... until"
// then the timeout will be in pdata->dwTimeout,
// otherwise pdata->dwTimeout will be INFINITE
// which is exactly how long we'll be waiting
//
RsPostReceive(pscript);
RASSCRPT_TRACE("RsThreadProcess: script waiting for input");
break; } else if (Astexec_IsPaused(pdata->pastexec)) {
//
// we're blocked with a timeout, so pick up
// the timeout value from pdata->dwTimeout.
// we don't want to listen for input
// while we're blocked, so we don't post another receive-request
//
RASSCRPT_TRACE("RsThreadProcess: script paused");
break; }
} while (TRUE);
return dwErr; }
//----------------------------------------------------------------------------
// Function: RxLogErrors
//
// Logs script syntax errors to a file named %windir%\system32\ras\script.log
//----------------------------------------------------------------------------
DWORD RxLogErrors( IN HANDLE hscript, IN HSA hsaStxerr ) {
HANDLE hfile; CHAR *pszPath; STXERR stxerr; SCRIPTDATA *pdata; SCRIPTCB *pscript = hscript; DWORD i, cel, dwErr, dwSize;
RASSCRPT_TRACE("RxLogErrors");
if (!pscript || !pscript->pdata) { return ERROR_INVALID_PARAMETER; }
pdata = pscript->pdata;
//
// get the pathname for the logfile
//
dwSize = ExpandEnvironmentStrings(c_szScriptLog, NULL, 0);
pszPath = Malloc((dwSize + 1) * sizeof(CHAR)); if (!pszPath) { return ERROR_NOT_ENOUGH_MEMORY; }
ExpandEnvironmentStrings(c_szScriptLog, pszPath, dwSize);
//
// create the file, overwriting it if it already exists
//
hfile = CreateFile( pszPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); Free(pszPath);
if (INVALID_HANDLE_VALUE == hfile) { dwErr = GetLastError(); RASSCRPT_TRACE1("error %d creating logfile", dwErr); return dwErr; }
//
// truncate the previous contents of the file, if any
//
SetFilePointer(hfile, 0, 0, FILE_BEGIN); SetEndOfFile(hfile);
//
// get the number of syntax errors
//
cel = SAGetCount(hsaStxerr);
//
// append each error to the file
//
for (i = 0; i < cel; i++) {
UINT ids; CHAR* pszErr; BOOL bRet = SAGetItem(hsaStxerr, i, &stxerr);
if (!bRet) { continue; }
ids = IdsFromRes(Stxerr_GetRes(&stxerr));
if (ids == 0) { continue; }
//
// format the error message
//
ConstructMessage( &pszErr, g_hinst, MAKEINTRESOURCE(ids), pdata->script.szPath, Stxerr_GetLine(&stxerr), Stxerr_GetLexeme(&stxerr) );
if (!pszErr) { continue; }
//
// write the message to the log file
//
dwSize = lstrlen(pszErr);
WriteFile(hfile, pszErr, lstrlen(pszErr), &dwSize, NULL);
WriteFile(hfile, "\r\n", 2, &dwSize, NULL);
//
// free the message pointer
//
GFree(pszErr); }
CloseHandle(hfile);
return 0; }
//----------------------------------------------------------------------------
// Function: RxReadFile
//
// Transfers data out of a RASMAN buffer into the circular buffer used
// by the Win9x scripting code
//----------------------------------------------------------------------------
BOOL RxReadFile( IN HANDLE hscript, IN BYTE* pBuffer, IN DWORD dwBufferSize, OUT DWORD* pdwBytesRead ) {
SCRIPTCB* pscript = (SCRIPTCB*)hscript; DWORD dwRecvSize = pscript->dwRecvSize - pscript->dwRecvRead;
RASSCRPT_TRACE("RxReadFile");
if (!pdwBytesRead) { return FALSE; }
*pdwBytesRead = 0; if ((INT)dwRecvSize <= 0) { return FALSE; }
if (!dwBufferSize) { return FALSE; }
*pdwBytesRead = min(dwBufferSize, dwRecvSize); CopyMemory( pBuffer, pscript->pRecvBuffer + pscript->dwRecvRead, *pdwBytesRead ); pscript->dwRecvRead += *pdwBytesRead;
RASSCRPT_TRACE2("RxReadFile(rr=%d,br=%d)",pscript->dwRecvRead,*pdwBytesRead);
return TRUE; }
//----------------------------------------------------------------------------
// Function: RxSetIPAddress
//
// Sets the IP address for the script's RAS entry
//----------------------------------------------------------------------------
DWORD RxSetIPAddress( IN HANDLE hscript, IN LPCSTR lpszAddress ) {
DWORD dwErr = NO_ERROR; SCRIPTCB *pscript = (SCRIPTCB *)hscript;
RASSCRPT_TRACE1("RxSetIPAddress: %s", lpszAddress);
EnterCriticalSection(&g_cs);
//
// Free the existing IP address, if any
//
Free0(pscript->pszIpAddress);
//
// Allocate space for a copy of the address
//
pscript->pszIpAddress = Malloc(lstrlen(lpszAddress) + 1);
if (!pscript->pszIpAddress) { dwErr = ERROR_NOT_ENOUGH_MEMORY; } else {
//
// Copy the new IP address
//
lstrcpy(pscript->pszIpAddress, lpszAddress); }
LeaveCriticalSection(&g_cs);
//
// If successful, signal the caller that the IP address has changed
//
if (dwErr != NO_ERROR) { RASSCRPT_TRACE1("error %d writing phonebook file", dwErr); } else {
if ( INVALID_HANDLE_VALUE != hIpAddressSet && !(pscript->dwFlags & RASSCRIPT_HwndNotify)) { DWORD dwEventCode = SCRIPTCODE_IpAddressSet; RASSCRPT_TRACE1("RxSetIPAddress: %d", dwEventCode);
InterlockedExchange(&pscript->dwEventCode, dwEventCode); SetEvent (hIpAddressSet);
}
else if (pscript->dwFlags & RASSCRIPT_HwndNotify) RsSignal(pscript, SCRIPTCODE_IpAddressSet); }
return dwErr; }
//----------------------------------------------------------------------------
// Function: RxSetKeyboard
//
// Signals the script-owner to enable or disable keyboard input.
//----------------------------------------------------------------------------
DWORD RxSetKeyboard( IN HANDLE hscript, IN BOOL bEnable ) {
RASSCRPT_TRACE("RxSetKeyboard");
RsSignal( (SCRIPTCB *)hscript, bEnable ? SCRIPTCODE_KeyboardEnable : SCRIPTCODE_KeyboardDisable );
return NO_ERROR; }
//----------------------------------------------------------------------------
// Function: RxSendCreds
//
// Sends users password over the wire.
//----------------------------------------------------------------------------
DWORD RxSendCreds( IN HANDLE hscript, IN CHAR controlchar ) {
SCRIPTCB *pscript = (SCRIPTCB *) hscript; DWORD dwErr;
RASSCRPT_TRACE("RasSendCreds");
dwErr = RasSendCreds(pscript->hport, controlchar);
RASSCRPT_TRACE1("RasSendCreds done. 0x%x", dwErr);
return (dwErr == NO_ERROR) ? RES_OK : RES_E_FAIL; }
//----------------------------------------------------------------------------
// Function: RxSetPortData
//
// Changes settings for the COM port.
//----------------------------------------------------------------------------
DWORD RxSetPortData( IN HANDLE hscript, IN VOID* pStatement ) {
RES res; STMT* pstmt; RAS_PARAMS* pparam; DWORD dwErr, dwFlags; RASMAN_PORTINFO *prmpi; SCRIPTCB *pscript = (SCRIPTCB *)hscript; BYTE aBuffer[sizeof(RASMAN_PORTINFO) + sizeof(RAS_PARAMS) * 2];
RASSCRPT_TRACE("RxSetPortData");
//
// Retrieve the 'set port' statement
//
pstmt = (STMT*)pStatement;
dwFlags = SetPortStmt_GetFlags(pstmt);
//
// Set up the RASMAN_PORTINFO to be passed to RasPortSetInfo
//
prmpi = (RASMAN_PORTINFO*)aBuffer;
prmpi->PI_NumOfParams = 0;
pparam = prmpi->PI_Params;
//
// Collect the changes into the port-info structure
//
if (IsFlagSet(dwFlags, SPF_DATABITS)) {
lstrcpyA(pparam->P_Key, SER_DATABITS_KEY);
pparam->P_Type = Number;
pparam->P_Attributes = 0;
pparam->P_Value.Number = SetPortStmt_GetDatabits(pstmt);
RASSCRPT_TRACE1("GetDatabits==%d", pparam->P_Value.Number);
++prmpi->PI_NumOfParams;
++pparam; }
if (IsFlagSet(dwFlags, SPF_STOPBITS)) {
lstrcpyA(pparam->P_Key, SER_STOPBITS_KEY);
pparam->P_Type = Number;
pparam->P_Attributes = 0;
pparam->P_Value.Number = SetPortStmt_GetStopbits(pstmt);
//
// The only 'stopbits' settings supported are 1 and 2;
// in order to set stopbits of 1, we need to pass 0
// to RasPortSetInfo, so the value is adjusted here.
//
if (pparam->P_Value.Number == 1) { --pparam->P_Value.Number; }
RASSCRPT_TRACE1("GetStopbits==%d", pparam->P_Value.Number);
++prmpi->PI_NumOfParams;
++pparam; }
if (IsFlagSet(dwFlags, SPF_PARITY)) {
lstrcpyA(pparam->P_Key, SER_PARITY_KEY);
pparam->P_Type = Number;
pparam->P_Attributes = 0;
pparam->P_Value.Number = SetPortStmt_GetParity(pstmt);
RASSCRPT_TRACE1("GetParity==%d", pparam->P_Value.Number);
++prmpi->PI_NumOfParams;
++pparam; }
//
// Send the changes down to RASMAN
//
if (!prmpi->PI_NumOfParams) { dwErr = NO_ERROR; } else {
dwErr = g_pRasPortSetInfo(pscript->hport, prmpi);
RASSCRPT_TRACE1("g_pRasPortSetInfo==%d", dwErr);
if (dwErr != NO_ERROR) {
Stxerr_Add( pscript->pdata->pastexec->hsaStxerr, "set port", Ast_GetLine(pstmt), RES_E_FAIL ); } }
return (dwErr == NO_ERROR) ? RES_OK : RES_E_FAIL; }
//----------------------------------------------------------------------------
// Function: RxWriteFile
//
// Transmits the given buffer thru RASMAN on a port
//----------------------------------------------------------------------------
VOID RxWriteFile( IN HANDLE hscript, IN BYTE* pBuffer, IN DWORD dwBufferSize, OUT DWORD* pdwBytesWritten ) {
RASSCRPT_TRACE("RxWriteFile");
if (!pdwBytesWritten) { return; }
RasScriptSend(hscript, pBuffer, dwBufferSize);
*pdwBytesWritten = dwBufferSize;
RASSCRPT_TRACE1("RxWriteFile(bw=%d)", *pdwBytesWritten); }
|