/*++
*******************************************************************************
* Copyright (c) 1990-2000 Digi International Inc., All Rights Reserved		  *
* 																			  *
* This software contains proprietary and confidential information of Digi	  *
* International Inc.  By accepting transfer of this copy, Recipient agrees	  *
* to retain this software in confidence, to prevent disclosure to others,	  *
* and to make no use of this software other than that for which it was		  *
* delivered.  This is an unpublished copyrighted work of Digi International	  *
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly	  *
* prohibited.																  *
* 																			  *
* Restricted Rights Legend													  *
*																			  *
* Use, duplication, or disclosure by the Government is subject to			  *
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in		  *
* Technical Data and Computer Software clause at DFARS 252.227-7031 or		  *
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -		  *
* Restricted Rights at 48 CFR 52.227-19, as applicable.						  *
*																			  *
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343		  *
*																			  *
*******************************************************************************

Module Name:

   x25evt.c

Abstract:

    Physical Layer MIB SNMP Extension Agent for Windows NT.

    These files (x25dll.c, x25mib.c, x25ect.c, x25sxb.h and x25mib.h) provide an 
    Extension Agent DLL which works in conjunction with the SNMP 
    Extendible Agent for Windows NT.

    This SNMP Extension Agent was derived from the sample on the Microsoft
    Developer's Toolkit. See the "Microsoft Windows/NT SNMP Programmer's Reference".

	This Extension Agent implements the Internet x25 MIB. It's definition is 
	found in the IETF RFC 1317.

Author:

   Carl Caldwell

--*/
#include <stdlib.h>
#include <snmp.h>

// Contains definitions for the table structure describing the MIB.  This
// is used in conjunction with testmib.c where the MIB requests are resolved.
#include "x25mib.h"

// Contains definitions for the structures for collecting data from the SXB
// driver interface.
#include "x25sxb.h"

// Buffer for formatted Debug messages
char	DbgBuf[80];

// Termination event handle
extern HANDLE	terminateEvent;

// Extension Agent DLLs need access to elapsed time agent has been active.
// This is implemented by initializing the Extension Agent with a time zero
// reference, and allowing the agent to compute elapsed time by subtracting
// the time zero reference from the current system time.  This example
// Extension Agent implements this reference with dwTimeZero.

extern DWORD dwTimeZero;

// This variable points to the MIB table that is to be worked upon by events
extern MIB_ENTRY Mib[];

// Extension Agent DLLs that generate traps must create a Win32 Event object
// to communicate occurence of traps to the Extendible Agent.  The event
// handle is given to the Extendible Agent when the Extension Agent is 
// initialized, it should be NULL if traps will not be generated.  This
// example Extension Agent simulates the occurance of traps with hSimulateTrap.
extern HANDLE hX25Trap;

// Extension Agent DLLs need access to elapsed time agent has been active.
// This is implemented by initializing the Extension Agent with a time zero
// reference, and allowing the agent to compute elapsed time by subtracting
// the time zero reference from the current system time.  This example
// Extension Agent implements this reference with dwTimeZero.
extern DWORD dwTimeZero;

// The X.25 Extension agent DLL needs to process restart and reset trap events
// when thay occur. These events are kept on a linked list. Whenever a trap 
// event occurs, the hX25Trap event will be signalled. The Extendible agent will
// then poll the driver at the SnmpExtensionTrap entry point until all events are 
// exhausted from the queue.
extern PX25SNMP_TrapListEntry pX25TrapListEntry;

// This structure ties a call parm mib entry to a circuit mib entry. This is used
// to track Call Parameter and Call Cleared events
extern PMIB_CircuitCallBindTable_t pCircuitCallBindTable;

//
// x25_Sxb_Trace_Data_Collection
//    Handles all the data needed to update the Call param and circuit MIB tables
//
// Notes:
//      This routine is spawned as a thread from the init routine. It runs
//  off the trace machanism in SXB.
//
// Return Codes:
//
// Error Codes:
//
void x25_Sxb_Trace_Data_Collection(
		IN PSXB_COLLECTOR_PARM_STRUCT pSxbParmInfo
		)
{
PSXB_PORT_INFO				pCurSxbPortInfo = pSxbParmInfo->pSxbPortInfoParm;
PX25_SXB_EVENT_IOCTL_INFO	pCurSxbAdapterIoctl = pSxbParmInfo->pSxbEventIoctlInfoParm;
PX25_SXB_EVENT_IOCTL_INFO	pOldSxbAdapterIoctl;
PX25_SXB_EVENT_IOCTL_INFO	pNewSxbAdapterIoctl;
BOOL						firstPass = TRUE;
BOOL						bSuccess;
BOOL						running = TRUE;
X_IOCTL						XIoctl;
HANDLE						fileHandles[16];
DWORD						handleCount = 0;
DWORD						eventIndex;
DWORD						ioctlIndex;
DWORD						overlappedBytesWritten;
uint32						traceEvent;
SNMP_trace_t *				pSNMP_TraceEvent;
DWORD						returnStatus=0;

	//DebugBreak();
	while (pCurSxbPortInfo != NULL )
	{
		if (pCurSxbPortInfo->firstPortOnAdapter)
		{
			DS_OPEN Open;
			OVERLAPPED Ol;
			// Build and Ioctl structure
			if (!firstPass)
			{
				// Allocate a new ioctl structure
				pCurSxbAdapterIoctl->pNextX25SxbIoctlInfo = SNMP_malloc(sizeof(X25_SXB_IOCTL_INFO));

				// Advance to the new structure
				pOldSxbAdapterIoctl = pCurSxbAdapterIoctl;
				pCurSxbAdapterIoctl = pCurSxbAdapterIoctl->pNextX25SxbIoctlInfo;
 				pCurSxbAdapterIoctl->pPreviousX25SxbIoctlInfo = pOldSxbAdapterIoctl;


			} //end if
			else
			{
				firstPass = FALSE;

				pCurSxbAdapterIoctl->pNextX25SxbIoctlInfo = NULL;
				pCurSxbAdapterIoctl->pPreviousX25SxbIoctlInfo = NULL;
			} //end else

			// Initialize the structure
			strcpy(pCurSxbAdapterIoctl->DeviceName, "\\\\.\\");
			strcat(pCurSxbAdapterIoctl->DeviceName, pCurSxbPortInfo->DeviceName);

			pCurSxbAdapterIoctl->IoctlType = PORT_IOCTL_NDX_CLEARED_CIRCUIT;
			pCurSxbAdapterIoctl->AdapterId = 0;
			pCurSxbAdapterIoctl->PortId = pCurSxbPortInfo->WANLinksPortId;
			pCurSxbAdapterIoctl->Snid = pCurSxbPortInfo->Snid;

			//The x25 statistics for the port are identified by REL_PORT_NAME
			StrToWStr(  pCurSxbAdapterIoctl->X25Name,
						ID_STR_SXB_X25_STATS,
						strlen(ID_STR_SXB_X25_STATS) + 1 );

			// Build the overlapped event for asynchronous IO completion
			pCurSxbAdapterIoctl->OverLappedIo.hEvent = CreateEvent( NULL,
																	TRUE,
																	FALSE,
																	NULL );

			// We are going to be waiting on multiple handles
			fileHandles[handleCount++] = pCurSxbAdapterIoctl->OverLappedIo.hEvent;
			
			pCurSxbAdapterIoctl->OverLappedIo.Internal = 0;
			pCurSxbAdapterIoctl->OverLappedIo.InternalHigh = 0;
			pCurSxbAdapterIoctl->OverLappedIo.Offset = 0;
			pCurSxbAdapterIoctl->OverLappedIo.OffsetHigh = 0;

			// Open a file handle to the adapter's first port
			if( (pCurSxbAdapterIoctl->hSxbPort = 
						CreateFile(  pCurSxbAdapterIoctl->DeviceName,
									 GENERIC_READ | GENERIC_WRITE,
									 FILE_SHARE_READ | FILE_SHARE_WRITE,
									 NULL, CREATE_ALWAYS,
									 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
									 NULL )) == INVALID_HANDLE_VALUE )
			{
				returnStatus = GetLastError();
#ifdef SNMP_DLL_DEBUG
				wsprintf(DbgBuf,
							"***E*** X.25: Event Trace Failure on file open of %s, Error=0x%x\n",
							pCurSxbAdapterIoctl->DeviceName,
							returnStatus);
				OutputDebugString(DbgBuf);
#endif
				return;
			} //end if

			
			// Open the USER SXB Port before enabling tracing
			//// Use the utility IOCTL to carry the payload to the driver
			ZeroMemory( &Open, sizeof( Open ) );
			strcpy( (PVOID)&Open.DeviceName, ID_STR_SXB_X25_STATS );
			ZeroMemory( &Ol, sizeof( Ol ) );
			Ol.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
			bSuccess = DeviceIoControl( pCurSxbAdapterIoctl->hSxbPort,
										(ULONG)IOCTL_DS_OPEN,
										&Open, sizeof( Open ),
										&Open, sizeof( Open ),
										&(pCurSxbAdapterIoctl->dwBytesWritten),
										&Ol );
			if ( !bSuccess )
			{
				if ( GetLastError() == ERROR_IO_PENDING )
				{
					bSuccess = GetOverlappedResult( 
						pCurSxbAdapterIoctl->hSxbPort, &Ol, 
						&(pCurSxbAdapterIoctl->dwBytesWritten), TRUE );
				}
			}
			pCurSxbAdapterIoctl->VCX25Device = Open.hStream;

			if( !bSuccess || Open.ErrorCode )
			{
				returnStatus = GetLastError();
#ifdef SNMP_DLL_DEBUG
				wsprintf(DbgBuf,
						 "***W*** X.25: Trace OPEN failed 0x%x, ErrorCode 0x%x\n",
							returnStatus, Open.ErrorCode );
				OutputDebugString(DbgBuf);
#endif
				return;
			} //end if
			
			// Enable tracing on the adapter's first port
			//// Build an enable tracing msg in the utility ioctl

			XIoctl.DsIoctl.Command = RFC_1382_TRACE_ON;
			XIoctl.DsIoctl.cbDataIn = XIoctl.DsIoctl.cbMaxDataOut = 0;
			XIoctl.DsIoctl.hStream = pCurSxbAdapterIoctl->VCX25Device;
			XIoctl.DsIoctl.pDataIn = XIoctl.DsIoctl.pDataOut = (PVOID)&XIoctl.Data;
			ZeroMemory( &Ol, sizeof( Ol ) );
			Ol.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );

			// Now that we've built an IOCTL, send it down to SXB
			bSuccess = DeviceIoControl( pCurSxbAdapterIoctl->hSxbPort, 
			                            (ULONG)IOCTL_DS_IOCTL,
			                            &XIoctl.DsIoctl, sizeof(DS_IOCTL),
			                            &XIoctl.DsIoctl, sizeof(DS_IOCTL),
			                            &(pCurSxbAdapterIoctl->dwBytesWritten),
			                            &Ol );

			// did the driver like it?
			if( !bSuccess )
			{
				if ( GetLastError() == ERROR_IO_PENDING )
				{
					bSuccess = GetOverlappedResult( 
						pCurSxbAdapterIoctl->hSxbPort, &Ol, 
						&pCurSxbAdapterIoctl->dwBytesWritten, TRUE );
				}
			}
			if ( !bSuccess )
			{
				//
				// There was some sort of problem enabling the trace the device!
				//
				returnStatus = GetLastError();
#ifdef SNMP_DLL_DEBUG
				wsprintf( DbgBuf,"S_IOCTL for trace failed, GetLastError = 0x%x\n",
									returnStatus);
				OutputDebugString(DbgBuf);
#endif
				return;
			} //end if
			else
			{	
				//did the firmware like it?
				if ( XIoctl.DsIoctl.ReturnValue || XIoctl.DsIoctl.ErrorCode )
				{
#ifdef SNMP_DLL_DEBUG
					OutputDebugString("***W*** X.25: Failure on RETURN of TRACE Enable\n");
#endif
				}
				else
				{
#ifdef SNMP_DLL_DEBUG
					OutputDebugString("***I*** X.25: RFC1382 TRACE Facility Enabled\n");
#endif
				}
			}

			// Send down a GETMSG ioctl for each board
			pCurSxbAdapterIoctl->XGetMsg.DsGetMsg.cbControl    = 0;
			pCurSxbAdapterIoctl->XGetMsg.DsGetMsg.cbMaxControl = X_GETMSG_CTRL_SIZE;
			pCurSxbAdapterIoctl->XGetMsg.DsGetMsg.cbData       = 0;
			pCurSxbAdapterIoctl->XGetMsg.DsGetMsg.cbMaxData    = X_GETMSG_DATA_SIZE;
			pCurSxbAdapterIoctl->XGetMsg.DsGetMsg.hStream      = pCurSxbAdapterIoctl->VCX25Device;
			pCurSxbAdapterIoctl->XGetMsg.DsGetMsg.pData        = (PVOID)&pCurSxbAdapterIoctl->XGetMsg.Data;
			pCurSxbAdapterIoctl->XGetMsg.DsGetMsg.pControl     = (PVOID)&pCurSxbAdapterIoctl->XGetMsg.Control;
			pCurSxbAdapterIoctl->dwBytesWritten = 0;
			
			bSuccess = DeviceIoControl( pCurSxbAdapterIoctl->hSxbPort,
										(ULONG)IOCTL_DS_GETMSG,
										&pCurSxbAdapterIoctl->XGetMsg.DsGetMsg,
										sizeof( DS_GETMSG ),
										&pCurSxbAdapterIoctl->XGetMsg.DsGetMsg,
										sizeof( DS_GETMSG ),
										&(pCurSxbAdapterIoctl->dwBytesWritten),
										&(pCurSxbAdapterIoctl->OverLappedIo));
			
			if (!bSuccess)
			{
				returnStatus = GetLastError();
				if (returnStatus != ERROR_IO_PENDING)
				{
#ifdef SNMP_DLL_DEBUG
					wsprintf(DbgBuf,"***E*** X25: Get User Message IOCTL failed, error = 0x%x\n",
									returnStatus);
					OutputDebugString(DbgBuf);
#endif
				}
			}
		} //end if

		// Look at the next port's information
		pCurSxbPortInfo = pCurSxbPortInfo->pNextSxbPortInfo;

	} //end while 

	// We have to add the terminateEvent's handle to the wait on me table.
	fileHandles[handleCount++] = terminateEvent;

	do {
		// Now all the ports have a GETMSG IOTCL built, the file and port is opened,
		// and the GETMSG ioctl has been sent. We must wait on all overlapped structures,
		// and the termination semaphore. Any of these events will trigger an action. A
		// terminate will cause a shutdown on all the ports, and an async IO completion will
		// cause a new AdapterIoctl structure to be constructed and posted, while the old one
		// is processed.

		do 
		{
			// Wait for an IO operation, or a terminate event to complete 
			eventIndex = WaitForMultipleObjects(handleCount,
												fileHandles,
												FALSE,
												100);	
		} while (eventIndex == WAIT_TIMEOUT);			
		wsprintf( DbgBuf,"Processing SXB Trace Event %d from total of %d\n", 
			eventIndex, handleCount );
#ifdef SNMP_DLL_DEBUG
		OutputDebugString(DbgBuf);
#endif
		// Adjust for the Wait Handle Offset
		eventIndex -= WAIT_OBJECT_0;

		bSuccess = ResetEvent(fileHandles[eventIndex]);

		if (!bSuccess)
		{
			//
			// There was some sort of problem resetting the event
			//
			wsprintf( DbgBuf,"ResetEvent for GetMsg Wait failed, GetLastError = 0x%x\n",
			  GetLastError() );
#ifdef SNMP_DLL_DEBUG
			OutputDebugString(DbgBuf);
#endif			
			return;
		}

		// Check to see if the file handles associated with the traces fired
		if ((eventIndex >= 0) && (eventIndex <= (handleCount - 2)))
		{
		//Reset the event that triggered the wait

			// Locate the IOCTL that finished the IO cycle
			for (ioctlIndex = 0, pCurSxbAdapterIoctl=pSxbParmInfo->pSxbEventIoctlInfoParm; 
				ioctlIndex < eventIndex; 
				ioctlIndex++,pCurSxbAdapterIoctl=pCurSxbAdapterIoctl->pNextX25SxbIoctlInfo)
			{
					//Let the for loop do the work
			}
			
			// Check the overlapped result on the file handle
			bSuccess = FALSE;
			do
			{
				bSuccess = GetOverlappedResult( pCurSxbAdapterIoctl->hSxbPort,
											&(pCurSxbAdapterIoctl->OverLappedIo),
											&(overlappedBytesWritten),
											FALSE );
				wsprintf( DbgBuf,"Overlapped IO for GetMsg returned %d bytes\n",
							overlappedBytesWritten);
#ifdef SNMP_DLL_DEBUG
				OutputDebugString(DbgBuf);
#endif
			} while (!bSuccess);
			

			//DebugBreak();
			// Decode the source of this ioctl and process it accordingly
			//// Allocate a new ioctl
			pNewSxbAdapterIoctl = SNMP_malloc(sizeof(X25_SXB_EVENT_IOCTL_INFO));
			pOldSxbAdapterIoctl = pCurSxbAdapterIoctl;

			//// Detach the completed ioctl from the chain and chain the new one in
			if (pCurSxbAdapterIoctl->pPreviousX25SxbIoctlInfo != NULL )
			{
				pCurSxbAdapterIoctl->pPreviousX25SxbIoctlInfo->pNextX25SxbIoctlInfo = 
					pNewSxbAdapterIoctl;
				pNewSxbAdapterIoctl->pPreviousX25SxbIoctlInfo = pCurSxbAdapterIoctl->pPreviousX25SxbIoctlInfo;
			}
			else
			{
				pNewSxbAdapterIoctl->pPreviousX25SxbIoctlInfo = NULL;

				//// If this is the first IOCTL, the base structure pointer needs to
				//// be updated to point to this one! (CHC 12/17/96)
				pSxbParmInfo->pSxbEventIoctlInfoParm = pNewSxbAdapterIoctl;
			}
			
			if ( pCurSxbAdapterIoctl->pNextX25SxbIoctlInfo != NULL )
			{
 				pCurSxbAdapterIoctl->pNextX25SxbIoctlInfo->pPreviousX25SxbIoctlInfo = 
					pNewSxbAdapterIoctl;	
				pNewSxbAdapterIoctl->pNextX25SxbIoctlInfo = pCurSxbAdapterIoctl->pNextX25SxbIoctlInfo;
			}
			else
			{
				pNewSxbAdapterIoctl->pNextX25SxbIoctlInfo = NULL;
			}


			//// Initialize the new ioctl from the old one
			strcpy(pNewSxbAdapterIoctl->DeviceName, pCurSxbAdapterIoctl->DeviceName);
			pNewSxbAdapterIoctl->IoctlType	= pCurSxbAdapterIoctl->IoctlType;
			pNewSxbAdapterIoctl->AdapterId	= pCurSxbAdapterIoctl->AdapterId;
			pNewSxbAdapterIoctl->hSxbPort	= pCurSxbAdapterIoctl->hSxbPort;
			pNewSxbAdapterIoctl->VCX25Device = pCurSxbAdapterIoctl->VCX25Device;
			pNewSxbAdapterIoctl->PortId		= pCurSxbAdapterIoctl->PortId;
			pNewSxbAdapterIoctl->Snid		= pCurSxbAdapterIoctl->Snid;

			//The x25 statistics for the port are identified by REL_PORT_NAME
			StrToWStr(  pNewSxbAdapterIoctl->X25Name,
						ID_STR_SXB_X25_STATS,
						strlen(ID_STR_SXB_X25_STATS) + 1 );

			// Build the overlapped event for asynchronous IO completion
			pNewSxbAdapterIoctl->OverLappedIo.Internal = 0;
			pNewSxbAdapterIoctl->OverLappedIo.InternalHigh = 0;
			pNewSxbAdapterIoctl->OverLappedIo.Offset = 0;
			pNewSxbAdapterIoctl->OverLappedIo.OffsetHigh = 0;
			pNewSxbAdapterIoctl->OverLappedIo.hEvent = 
				pCurSxbAdapterIoctl->OverLappedIo.hEvent;

			pNewSxbAdapterIoctl->XGetMsg.DsGetMsg.cbControl    = 0;
			pNewSxbAdapterIoctl->XGetMsg.DsGetMsg.cbMaxControl = X_GETMSG_CTRL_SIZE;
			pNewSxbAdapterIoctl->XGetMsg.DsGetMsg.cbData       = 0;
			pNewSxbAdapterIoctl->XGetMsg.DsGetMsg.cbMaxData    = X_GETMSG_DATA_SIZE;
			pNewSxbAdapterIoctl->XGetMsg.DsGetMsg.pData        = (PVOID)&pNewSxbAdapterIoctl->XGetMsg.Data;
			pNewSxbAdapterIoctl->XGetMsg.DsGetMsg.pControl     = (PVOID)&pNewSxbAdapterIoctl->XGetMsg.Control;
			pNewSxbAdapterIoctl->XGetMsg.DsGetMsg.hStream      = pNewSxbAdapterIoctl->VCX25Device;

			// Send down a NEW GETMSG ioctl for THIS board
			pNewSxbAdapterIoctl->dwBytesWritten = 0;
			bSuccess = DeviceIoControl( pNewSxbAdapterIoctl->hSxbPort,
										(ULONG)IOCTL_DS_GETMSG,
										&pNewSxbAdapterIoctl->XGetMsg.DsGetMsg,
										sizeof( DS_GETMSG ),
										&pNewSxbAdapterIoctl->XGetMsg.DsGetMsg,
										sizeof( DS_GETMSG ),
										&(pNewSxbAdapterIoctl->dwBytesWritten),
										&(pNewSxbAdapterIoctl->OverLappedIo));
			if (!bSuccess)
			{
				returnStatus = GetLastError();
				if (returnStatus != ERROR_IO_PENDING)
				{
#ifdef SNMP_DLL_DEBUG
					wsprintf(DbgBuf,"***E*** X25: Get User Message IOCTL failed, error = 0x%x\n",
									returnStatus);
					OutputDebugString(DbgBuf);
#endif
				}
			}

			//// Either process the old IOCTL, or dispose of it
			// Decide what type of EVENT this is
			pSNMP_TraceEvent = (SNMP_trace_t *)(&pCurSxbAdapterIoctl->XGetMsg.Control);
			traceEvent = pSNMP_TraceEvent->Event;

			switch (traceEvent){
			case SNMP_EVENT_CLEARED_CIRCUIT:
#ifdef SNMP_DLL_DEBUG
				OutputDebugString("X25: Process Cleared Circuit Event\n");
#endif
				bSuccess = x25_Process_Cleared_Circuit_Event(pCurSxbAdapterIoctl);
				break;
			case SNMP_EVENT_CALL_PARM:
#ifdef SNMP_DLL_DEBUG
				OutputDebugString("X25: Process Call Parm Event\n");
#endif
				bSuccess = x25_Process_Call_Parm_Event(pCurSxbAdapterIoctl);
				break;
			case SNMP_EVENT_RESTART:
#ifdef SNMP_DLL_DEBUG
				OutputDebugString("X25: Process Restart Event\n");
#endif
				bSuccess = x25_Process_Restart_Event(pCurSxbAdapterIoctl);
				break;
			case SNMP_EVENT_RESET:
#ifdef SNMP_DLL_DEBUG
				OutputDebugString("X25: Process Reset Event\n");
#endif
				bSuccess = x25_Process_Reset_Event(pCurSxbAdapterIoctl);
				break;
			default:
#ifdef SNMP_DLL_DEBUG
				wsprintf( DbgBuf,"***E*** X25: UNKNOWN Event=0x%x Received\n", traceEvent);
				OutputDebugString(DbgBuf);
#endif
				SNMP_free(pCurSxbAdapterIoctl);
				break;
			}
		}
		// Check to see if we received a terminate indication
		else if (eventIndex == (handleCount - 1))
		{
			bSuccess = ResetEvent(fileHandles[eventIndex]);
#ifdef SNMP_DLL_DEBUG
			wsprintf( DbgBuf,"Received the terminate event\n");
			OutputDebugString(DbgBuf);
#endif
			running = FALSE;
		}
		// Check for any other cases of interest
		else
		{
			bSuccess = ResetEvent(fileHandles[eventIndex]);
#ifdef SNMP_DLL_DEBUG
			wsprintf( DbgBuf,"***E*** Received an UNKNOWN TRACE event = 0x%x\n",
						eventIndex);
			OutputDebugString(DbgBuf);
#endif
		}
	} while (running);
}

//
// x25_Terminate_Sxb_Trace_Data_Collection
//    Stops the processing of trace data collection
//
// Notes:
//      This routine is spawned as a thread from the init routine. It runs
//  off the trace machanism in SXB.
//
// Return Codes:
//
// Error Codes:
//
void x25_Terminate_Sxb_Trace_Data_Collection(void)
{
	if (terminateEvent != NULL)
	{
		SetEvent(terminateEvent); //This will notify the collection threads
	}
	else
	{
#ifdef SNMP_DLL_DEBUG
		OutputDebugString("***E*** Terminate Event Handle is INVALID for DLLProcess Detach\n");
#endif
	}
}

//
// x25_Process_Cleared_Circuit_Event
//    Handles all the data needed to update the Call param and circuit MIB tables
//
// Notes:
//
// Return Codes:
//	TRUE = success
//	FALSE = failure
//
// Error Codes:
//
BOOL x25_Process_Cleared_Circuit_Event(
		IN PX25_SXB_EVENT_IOCTL_INFO	pX25SxbAdapterIoctl
		)
{
BOOL	mStatus = TRUE;
BOOL	bSuccess;
PMIB_ENTRY	pClearedCircuitMibEntry = &Mib[X25_CLEARED_CIRCUIT_FIRST_Index];
SNMP_ClearedCircuit_Event_t *	pClearedCircuitEvent;

	pClearedCircuitEvent = 
		((SNMP_ClearedCircuit_Event_t *)(&pX25SxbAdapterIoctl->XGetMsg.Control));

	*((long *)pClearedCircuitMibEntry->Storage) = 
		(AsnInteger)pClearedCircuitEvent->X25ClearedCircuitIndex;
	pClearedCircuitMibEntry = pClearedCircuitMibEntry->NextMibColumn;

	*((long *)pClearedCircuitMibEntry->Storage) = 
		(AsnInteger)pClearedCircuitEvent->X25ClearedCircuitPleIndex;
	pClearedCircuitMibEntry = pClearedCircuitMibEntry->NextMibColumn;


	*((long *)pClearedCircuitMibEntry->Storage) = 
		(AsnInteger)pClearedCircuitEvent->X25ClearedCircuitTimeEstablished;
	pClearedCircuitMibEntry = pClearedCircuitMibEntry->NextMibColumn;

	*((long *)pClearedCircuitMibEntry->Storage) = 
		(AsnInteger)pClearedCircuitEvent->X25ClearedCircuitTimeCleared;
	pClearedCircuitMibEntry = pClearedCircuitMibEntry->NextMibColumn;

	*((long *)pClearedCircuitMibEntry->Storage) = 
		(AsnInteger)pClearedCircuitEvent->X25ClearedCircuitChannel;
	pClearedCircuitMibEntry = pClearedCircuitMibEntry->NextMibColumn;

	*((long *)pClearedCircuitMibEntry->Storage) = 
		(AsnInteger)pClearedCircuitEvent->X25ClearedCircuitClearingCause;
	pClearedCircuitMibEntry = pClearedCircuitMibEntry->NextMibColumn;

	*((long *)pClearedCircuitMibEntry->Storage) = 
		(AsnInteger)pClearedCircuitEvent->X25ClearedCircuitDiagnosticCode;
	pClearedCircuitMibEntry = pClearedCircuitMibEntry->NextMibColumn;

	*((long *)pClearedCircuitMibEntry->Storage) = 
		(AsnInteger)pClearedCircuitEvent->X25ClearedCircuitInPDUs;
	pClearedCircuitMibEntry = pClearedCircuitMibEntry->NextMibColumn;

	*((long *)pClearedCircuitMibEntry->Storage) = 
		(AsnInteger)pClearedCircuitEvent->X25ClearedCircuitOutPDUs;
	pClearedCircuitMibEntry = pClearedCircuitMibEntry->NextMibColumn;

	bSuccess = MibByteStreamToOctetStringCopy(
					(AsnOctetString*)pClearedCircuitMibEntry->Storage,
					pClearedCircuitEvent->X25ClearedCircuitCalledAddress,
					X25_CALL_ADDRESS_SIZE
				);
	pClearedCircuitMibEntry = pClearedCircuitMibEntry->NextMibColumn;

	bSuccess = MibByteStreamToOctetStringCopy(
					(AsnOctetString*)pClearedCircuitMibEntry->Storage,
					pClearedCircuitEvent->X25ClearedCircuitCallingAddress,
					X25_CALL_ADDRESS_SIZE
				);
	pClearedCircuitMibEntry = pClearedCircuitMibEntry->NextMibColumn;

	bSuccess = MibByteStreamToOctetStringCopy(
					(AsnOctetString*)pClearedCircuitMibEntry->Storage,
					pClearedCircuitEvent->X25ClearedCircuitClearFacilities,
					X25_FACILITIES_SIZE
				);
	pClearedCircuitMibEntry = pClearedCircuitMibEntry->NextMibColumn;

	//Update the cleared circuit entries requested variable
	pClearedCircuitMibEntry = &Mib[X25_CLEARED_CIRCUIT_EntriesRequested_NDX];
	*((AsnInteger *)pClearedCircuitMibEntry->Storage)++;
	
	//Update the cleared circuit entries granted variable
	pClearedCircuitMibEntry = &Mib[X25_CLEARED_CIRCUIT_EntriesGranted_NDX];
	*((AsnInteger *)pClearedCircuitMibEntry->Storage)++;
	
	SNMP_free (pX25SxbAdapterIoctl);
	return mStatus;
}

//
// x25_Process_Call_Parm_Event
//    Handles all processing of the Call param event 
//
// Notes:
//
// Return Codes:
//	TRUE = success
//	FALSE = failure
//
// Error Codes:
//
BOOL x25_Process_Call_Parm_Event(
		IN PX25_SXB_EVENT_IOCTL_INFO	pX25SxbAdapterIoctl
		)
{
BOOL						found = FALSE;
BOOL						mStatus = TRUE;
PX_GETMSG					pSxbGetMsg;
SNMP_CallParm_Event_t *		pCallParmEvent;
AsnInteger					cktCarryingCall;
PMIB_CircuitCallBindTable_t	pCurCircuitCallBindTable;
PMIB_ENTRY					pCallParmMibEntry;
BOOL						bSuccess;

	// Grab onto the user data
	pCallParmEvent = 
		(SNMP_CallParm_Event_t *)(&pX25SxbAdapterIoctl->XGetMsg.Control);

	// Check that the table is OK
	if ((pCurCircuitCallBindTable = pCircuitCallBindTable) == NULL)
	{
		mStatus = FALSE;
	}
	else
	{
		// Locate the circuit associated with the call
		pSxbGetMsg		= &pX25SxbAdapterIoctl->XGetMsg;
		pCallParmEvent	= (SNMP_CallParm_Event_t *)&pSxbGetMsg->Control;
		cktCarryingCall	= pCallParmEvent->X25CallParmChannel;

		// Locate the Mib Entry associated with the call params
		while (!found)
		{
			if (cktCarryingCall == pCurCircuitCallBindTable->Circuit)
			{
				found = TRUE;
			}
			else
			{
				pCurCircuitCallBindTable = pCurCircuitCallBindTable->pNextCircuitCallBindTable;
				if (pCurCircuitCallBindTable == NULL)
				{
					break;
				}
			}
		}

		if (!found)
		{
			mStatus = FALSE;
		}
		else
		{
			//// Update the call params for the circuit /////
			// Using the Call Param Table Entry,assign the variables from the 
			// Event to the Call Param Table Entry. Since the MIB vars are 
			// chained bidirectionally (at least in the CallParm table),
			// you can walk down the column and copy in the appropriate variables
			pCallParmMibEntry = pCurCircuitCallBindTable->pCallParmMib;

			*((long *)pCallParmMibEntry->Storage) = (AsnInteger)pCallParmEvent->X25CallParmIndex;
			pCallParmMibEntry = pCallParmMibEntry->NextMibColumn;
			
			*((long *)pCallParmMibEntry->Storage) = (AsnInteger)pCallParmEvent->X25CallParmStatus;
			pCallParmMibEntry = pCallParmMibEntry->NextMibColumn;

			*((long *)pCallParmMibEntry->Storage) = (AsnInteger)pCallParmEvent->X25CallParmRefCount;
			pCallParmMibEntry = pCallParmMibEntry->NextMibColumn;

			*((long *)pCallParmMibEntry->Storage) = (AsnInteger)pCallParmEvent->X25CallParmInPacketSize;
			pCallParmMibEntry = pCallParmMibEntry->NextMibColumn;

			*((long *)pCallParmMibEntry->Storage) = (AsnInteger)pCallParmEvent->X25CallParmOutPacketSize;
			pCallParmMibEntry = pCallParmMibEntry->NextMibColumn;

			*((long *)pCallParmMibEntry->Storage) = (AsnInteger)pCallParmEvent->X25CallParmInWindowSize;
			pCallParmMibEntry = pCallParmMibEntry->NextMibColumn;

			*((long *)pCallParmMibEntry->Storage) = (AsnInteger)pCallParmEvent->X25CallParmOutWindowSize;
			pCallParmMibEntry = pCallParmMibEntry->NextMibColumn;

			*((long *)pCallParmMibEntry->Storage) = (AsnInteger)pCallParmEvent->X25CallParmAcceptReverseCharging;
			pCallParmMibEntry = pCallParmMibEntry->NextMibColumn;

			*((long *)pCallParmMibEntry->Storage) = (AsnInteger)pCallParmEvent->X25CallParmProposeReverseCharging;
			pCallParmMibEntry = pCallParmMibEntry->NextMibColumn;

			*((long *)pCallParmMibEntry->Storage) = (AsnInteger)pCallParmEvent->X25CallParmFastSelect;
			pCallParmMibEntry = pCallParmMibEntry->NextMibColumn;

			*((long *)pCallParmMibEntry->Storage) = (AsnInteger)pCallParmEvent->X25CallParmInThruputClassSize;
			pCallParmMibEntry = pCallParmMibEntry->NextMibColumn;

			*((long *)pCallParmMibEntry->Storage) = (AsnInteger)pCallParmEvent->X25CallParmOutThruputClassSize;
			pCallParmMibEntry = pCallParmMibEntry->NextMibColumn;

			bSuccess = MibByteStreamToOctetStringCopy(
							(AsnOctetString *)pCallParmMibEntry->Storage,
							pCallParmEvent->X25CallParmCUG,
							X25_PRIORITY_SIZE
						);
			pCallParmMibEntry = pCallParmMibEntry->NextMibColumn;


			bSuccess = MibByteStreamToOctetStringCopy(
							(AsnOctetString *)pCallParmMibEntry->Storage,
							pCallParmEvent->X25CallParmCUGOA,
							X25_PRIORITY_SIZE
						);
			pCallParmMibEntry = pCallParmMibEntry->NextMibColumn;

			bSuccess = MibByteStreamToOctetStringCopy(
							(AsnOctetString *)pCallParmMibEntry->Storage,
							pCallParmEvent->X25CallParmBCUG,
							X25_BCUG_SIZE
						);
			pCallParmMibEntry = pCallParmMibEntry->NextMibColumn;

			bSuccess = MibByteStreamToOctetStringCopy(
							(AsnOctetString *)pCallParmMibEntry->Storage,
							pCallParmEvent->X25CallParmNUI,
							X25_FACILITIES_SIZE
						);
			pCallParmMibEntry = pCallParmMibEntry->NextMibColumn;

			*((long *)pCallParmMibEntry->Storage) = (AsnInteger)pCallParmEvent->X25CallParmChargingInfo;
			pCallParmMibEntry = pCallParmMibEntry->NextMibColumn;

			bSuccess = MibByteStreamToOctetStringCopy(
							(AsnOctetString *)pCallParmMibEntry->Storage,
							pCallParmEvent->X25CallParmRPOA,
							X25_FACILITIES_SIZE
						);
			pCallParmMibEntry = pCallParmMibEntry->NextMibColumn;

			*((long *)pCallParmMibEntry->Storage) = (AsnInteger)pCallParmEvent->X25CallParmTrnstDly;
			pCallParmMibEntry = pCallParmMibEntry->NextMibColumn;

			bSuccess = MibByteStreamToOctetStringCopy(
							(AsnOctetString *)pCallParmMibEntry->Storage,
							pCallParmEvent->X25CallParmCallingExt,
							X25_EXTENSION_SIZE
						);
			pCallParmMibEntry = pCallParmMibEntry->NextMibColumn;

			bSuccess = MibByteStreamToOctetStringCopy(
							(AsnOctetString *)pCallParmMibEntry->Storage,
							pCallParmEvent->X25CallParmCalledExt,
							X25_EXTENSION_SIZE
						);
			pCallParmMibEntry = pCallParmMibEntry->NextMibColumn;

			*((long *)pCallParmMibEntry->Storage) = (AsnInteger)pCallParmEvent->X25CallParmInMinThruputCls;
			pCallParmMibEntry = pCallParmMibEntry->NextMibColumn;

			*((long *)pCallParmMibEntry->Storage) = (AsnInteger)pCallParmEvent->X25CallParmOutMinThruputCls;
			pCallParmMibEntry = pCallParmMibEntry->NextMibColumn;


			bSuccess = MibByteStreamToOctetStringCopy(
							(AsnOctetString *)pCallParmMibEntry->Storage,
							pCallParmEvent->X25CallParmEndTrnsDly,
							X25_PRIORITY_SIZE
						);
			pCallParmMibEntry = pCallParmMibEntry->NextMibColumn;

			bSuccess = MibByteStreamToOctetStringCopy(
							(AsnOctetString *)pCallParmMibEntry->Storage,
							pCallParmEvent->X25CallParmPriority,
							X25_PRIORITY_SIZE
						);
			pCallParmMibEntry = pCallParmMibEntry->NextMibColumn;

			bSuccess = MibByteStreamToOctetStringCopy(
							(AsnOctetString *)pCallParmMibEntry->Storage,
							pCallParmEvent->X25CallParmProtection,
							X25_FACILITIES_SIZE
						);
			pCallParmMibEntry = pCallParmMibEntry->NextMibColumn;


			*((long *)pCallParmMibEntry->Storage) = (AsnInteger)pCallParmEvent->X25CallParmExptData;
			pCallParmMibEntry = pCallParmMibEntry->NextMibColumn;

		
			bSuccess = MibByteStreamToOctetStringCopy(
							(AsnOctetString *)pCallParmMibEntry->Storage,
							pCallParmEvent->X25CallParmUserData,
							X25_USER_DATA_SIZE
						);
			pCallParmMibEntry = pCallParmMibEntry->NextMibColumn;

			bSuccess = MibByteStreamToOctetStringCopy(
							(AsnOctetString *)pCallParmMibEntry->Storage,
							pCallParmEvent->X25CallParmCallingNetworkFacilities,
							X25_FACILITIES_SIZE
						);
			pCallParmMibEntry = pCallParmMibEntry->NextMibColumn;

			bSuccess = MibByteStreamToOctetStringCopy(
							(AsnOctetString *)pCallParmMibEntry->Storage,
							pCallParmEvent->X25CallParmCalledNetworkFacilities,
							X25_FACILITIES_SIZE
						);
			pCallParmMibEntry = pCallParmMibEntry->NextMibColumn;

		}
	}
	SNMP_free (pX25SxbAdapterIoctl);
	return mStatus;
}

//
// x25_Process_Restart_Event
//    Handles all the processing of the restart event
//
// Notes:
//
// Return Codes:
//	TRUE = success
//	FALSE = failure
//
// Error Codes:
//
BOOL x25_Process_Restart_Event(
		IN PX25_SXB_EVENT_IOCTL_INFO	pX25SxbAdapterIoctl
		)
{
BOOL					mStatus = TRUE;
uint32					sxbOperIndex;
UINT					x25OperIndex[] = { 1, 3, 6, 1, 2, 1, 10, 5, 2, 1, 1, 0};
AsnObjectIdentifier		x25OperIndexOid = { OID_SIZEOF(x25OperIndex), x25OperIndex };
UINT					lastOidDigitIndex = (sizeof(x25OperIndex)/sizeof(UINT))-1;
PX25SNMP_TrapListEntry	pX25LastTrapListEntry = pX25TrapListEntry;
PX25SNMP_TrapListEntry	pX25NewTrapListEntry;
RFC1157VarBindList *	pVarBindList;
UINT					returnCode;

	// The restart event will contain the X25OperIndex of the port receiving the 
	// restart request. It is packaged up into an RFC1157 VarBind list and placed
	// on the X25TraceEventList queue.
	sxbOperIndex = ((SNMP_Restart_Event_t*)&(pX25SxbAdapterIoctl->XGetMsg.Control))->X25OperIndex;
	
	// Normalize the Port Index for MIB
	sxbOperIndex = sxbOperIndex + 1 - (uint32)'A' ;

	x25OperIndex[lastOidDigitIndex] = sxbOperIndex;

	// Allocate a VarBind structure
	pVarBindList		= SNMP_malloc(sizeof(RFC1157VarBindList));
	pVarBindList->list	= SNMP_malloc(sizeof(RFC1157VarBind));
	pVarBindList->len	= 1;

	// The SNMP OID copy routine will allocate memory as necessary
    SNMP_oidcpy( &pVarBindList->list->name, &x25OperIndexOid );
	dbgPrintVarBindList ( pVarBindList );

	// Get the value of the variable associated with the OID
	returnCode = ResolveVarBind( pVarBindList->list, MIB_ACTION_GET );

    if ( returnCode != SNMP_ERRORSTATUS_NOERROR )
    {
		wsprintf( DbgBuf,"***E*** The RESTART EVENT failed var resolution with code = %d\n", returnCode);
#ifdef SNMP_DLL_DEBUG
		OutputDebugString(DbgBuf);
#endif
	}
	else
	{
		// Allocate and fill out a trace event structure
		pX25NewTrapListEntry						= SNMP_malloc(sizeof(X25SNMP_TrapListEntry));
		pX25NewTrapListEntry->specificTrap			= X25_TRAP_TYPE_RESTART;
		pX25NewTrapListEntry->timeStamp				= GetCurrentTime() - dwTimeZero;
		pX25NewTrapListEntry->variableBindingList	= pVarBindList;
		pX25NewTrapListEntry->pNextTrapListEntry	= NULL;

		if (pX25LastTrapListEntry == NULL)
		{
			pX25LastTrapListEntry = pX25NewTrapListEntry;
		}
		else
		{
			// Attach the trace Event structure to the end of the Trace Event List
			while (pX25LastTrapListEntry->pNextTrapListEntry != NULL)
			{
				pX25LastTrapListEntry = pX25LastTrapListEntry->pNextTrapListEntry;
			}
			pX25LastTrapListEntry->pNextTrapListEntry = pX25NewTrapListEntry;
		}

		// Signal the extendible agent that we have something to look at
		SetEvent(hX25Trap);
	}
	SNMP_free (pX25SxbAdapterIoctl);
	return mStatus;
}

//
// x25_Process_Reset_Event
//    Handles the reset requests
//
// Notes:
//
// Return Codes:
//	TRUE = success
//	FALSE = failure
//
// Error Codes:
//
BOOL x25_Process_Reset_Event(
		IN PX25_SXB_EVENT_IOCTL_INFO	pX25SxbAdapterIoctl
		)
{
BOOL					mStatus = TRUE;

uint32					sxbCircuitIndex;
UINT					x25CircuitIndex[] = { 1, 3, 6, 1, 2, 1, 10, 5, 5, 1, 1, 0};
AsnObjectIdentifier		x25CircuitIndexOid = { OID_SIZEOF(x25CircuitIndex), x25CircuitIndex };
UINT					lastCircuitOidDigitIndex = (sizeof(x25CircuitIndex)/sizeof(UINT))-1;

uint32					sxbChannelIndex;
UINT					x25ChannelIndex[] = { 1, 3, 6, 1, 2, 1, 10, 5, 4, 1, 1, 0};
AsnObjectIdentifier		x25ChannelIndexOid = { OID_SIZEOF(x25ChannelIndex), x25ChannelIndex };
UINT					lastChannelOidDigitIndex = (sizeof(x25ChannelIndex)/sizeof(UINT))-1;

PX25SNMP_TrapListEntry	pX25LastTrapListEntry = pX25TrapListEntry;
PX25SNMP_TrapListEntry	pX25NewTrapListEntry;
RFC1157VarBindList *	pVarBindList;
UINT					returnCode;

	// The restart event will contain the X25CircuitIndex, and X25ChannelIndex  of 
	// the port receiving the restart request. It is packaged up into an RFC1157 
	// VarBind list and placed on the X25TraceEventList queue.
	sxbCircuitIndex = ((SNMP_Reset_Event_t*)&(pX25SxbAdapterIoctl->XGetMsg.Control))->X25CircuitIndex;
	x25CircuitIndex[lastCircuitOidDigitIndex] = sxbCircuitIndex;

	sxbChannelIndex = ((SNMP_Reset_Event_t*)&(pX25SxbAdapterIoctl->XGetMsg.Control))->X25CircuitChannel;
	x25ChannelIndex[lastChannelOidDigitIndex] = sxbChannelIndex;

	// Allocate a VarBind structure
	pVarBindList		= SNMP_malloc(sizeof(RFC1157VarBindList));
	pVarBindList->list	= SNMP_malloc(2 * sizeof(RFC1157VarBind));
	pVarBindList->len	= 2;

	// The SNMP OID copy routine will allocate memory as necessary
    SNMP_oidcpy( &pVarBindList->list[0].name, &x25CircuitIndexOid );

	// Get the value of the variable associated with the OID
	returnCode = ResolveVarBind( &pVarBindList->list[0], MIB_ACTION_GET );
    if ( returnCode != SNMP_ERRORSTATUS_NOERROR )
    {
		wsprintf( DbgBuf,"***E*** The RESET EVENT failed var 0 resolution with code = %d\n", returnCode);
#ifdef SNMP_DLL_DEBUG
		OutputDebugString(DbgBuf);
#endif
	}
	 
	// The SNMP OID copy routine will allocate memory as necessary
    SNMP_oidcpy( &pVarBindList->list[1].name, &x25ChannelIndexOid );
	dbgPrintVarBindList ( pVarBindList );

	// Get the value of the variable associated with the OID
	returnCode = ResolveVarBind( &pVarBindList->list[1], MIB_ACTION_GET );
    if ( returnCode != SNMP_ERRORSTATUS_NOERROR )
    {
		wsprintf( DbgBuf,"***E*** The RESET EVENT failed var 1 resolution with code = %d\n", returnCode);
#ifdef SNMP_DLL_DEBUG
		OutputDebugString(DbgBuf);
#endif
	}
	else
	{
		// Allocate and fill out a trace event structure
		pX25NewTrapListEntry						= SNMP_malloc(sizeof(X25SNMP_TrapListEntry));
		pX25NewTrapListEntry->specificTrap			= X25_TRAP_TYPE_RESTART;
		pX25NewTrapListEntry->timeStamp				= GetCurrentTime() - dwTimeZero;
		pX25NewTrapListEntry->variableBindingList	= pVarBindList;
		pX25NewTrapListEntry->pNextTrapListEntry	= NULL;

		if (pX25LastTrapListEntry == NULL)
		{
			pX25LastTrapListEntry = pX25NewTrapListEntry;
		}
		else
		{
			// Attach the trace Event structure to the end of the Trace Event List
			while (pX25LastTrapListEntry->pNextTrapListEntry != NULL)
			{
				pX25LastTrapListEntry = pX25LastTrapListEntry->pNextTrapListEntry;
			}
			pX25LastTrapListEntry->pNextTrapListEntry = pX25NewTrapListEntry;
		}

		// Signal the extendible agent that we have something to look at
		SetEvent(hX25Trap);
	}
	SNMP_free (pX25SxbAdapterIoctl);
	return mStatus;
}

// Extension Agent DLLs provide the following entry point to communicate traps
// to the Extendible Agent.  The Extendible Agent will query this entry point
// when the trap Event (supplied at initialization time) is asserted, which
// indicates that zero or more traps may have occured.  The Extendible Agent 
// will repetedly call this entry point until FALSE is returned, indicating 
// that all outstanding traps have been processed.
BOOL WINAPI SnmpExtensionTrap(
    OUT AsnObjectIdentifier *enterprise,
    OUT AsnInteger          *genericTrap,
    OUT AsnInteger          *specificTrap,
    OUT AsnTimeticks        *timeStamp,
    OUT RFC1157VarBindList  *variableBindings)
{

PX25SNMP_TrapListEntry pX25OldTrapListEntry;

static UINT OidList[]  = { 1, 3, 6, 1, 2, 1, 10, 5 };
static UINT OidListLen = sizeof(OidList)/sizeof(UINT);

	// Check the event queue for anything to do
	if (pX25TrapListEntry == NULL)
	{
        // Indicate that no more traps are available and parameters do not
        // refer to any valid data.
        return FALSE;
	}
	else
	{
		// Communicate the trap data to the Extendible Agent.
        enterprise->idLength = OidListLen;
        enterprise->ids = OidList;

		// Copy over direct variables contained in the current list entry
        *genericTrap	= SNMP_GENERICTRAP_ENTERSPECIFIC;
        *specificTrap	= pX25TrapListEntry->specificTrap;
        *timeStamp		= pX25TrapListEntry->timeStamp;

		// Copy over the variable bindings list
		// It is assumed that these lists will be destroyed by the extendible agent
		// The only construction/destruction that will occur here is to the actual
		// trap list entry elements.
        variableBindings->len  = pX25TrapListEntry->variableBindingList->len;
        variableBindings->list = pX25TrapListEntry->variableBindingList->list;

		// Advance to the next element in the trap list
		pX25OldTrapListEntry = pX25TrapListEntry;
		pX25TrapListEntry	= pX25TrapListEntry->pNextTrapListEntry;

		// Delete the trap entry just returned
		SNMP_free( pX25OldTrapListEntry );

        // Indicate that valid trap data exists in the parameters.
        return TRUE;
    }
} // end SnmpExtensionTrap()


// The following utility will copy a byte string from src to dest, and return
// the size of the octet string.
BOOL MibByteStreamToOctetStringCopy(
			OUT	AsnOctetString *	pDstAsnOctetString,
			IN	BYTE *				pSrcByteStream,
			IN	UINT				streamSize
		)
{
UINT	ndx;

	// See if you need to allocate a larger storage bin
	if (pDstAsnOctetString->stream != NULL)
	{
		SNMP_free(pDstAsnOctetString->stream);
	}

	pDstAsnOctetString->stream = SNMP_malloc(streamSize);
	if (pDstAsnOctetString->stream == NULL)
	{
		// If you can't create the new byte string, return 0
		//DebugBreak();
		return FALSE;
	}

	for (ndx = 0; ndx < streamSize; ndx++)
	{
		pDstAsnOctetString->stream[ndx] = pSrcByteStream[ndx];
	}

	pDstAsnOctetString->length = streamSize;	
	return TRUE;
}

VOID dbgPrintVarBindList ( IN RFC1157VarBindList *	pVarBindList )
{
#ifdef SNMP_DLL_DEBUG
UINT				dbgPrintListNdx;
UINT				dbgPrintListEntryNdx;
RFC1157VarBind *	pVarBind;
UINT *				pObjectName;

	wsprintf(DbgBuf,"Variable Binding List containing %d elements:\n", pVarBindList->len);
	for (dbgPrintListNdx = 0; dbgPrintListNdx < pVarBindList->len; dbgPrintListNdx++ )
	{
		pVarBind = pVarBindList->list;
		wsprintf(DbgBuf,"\tObject Name = ");
		OutputDebugString(DbgBuf);
		for (dbgPrintListEntryNdx = 0, pObjectName = pVarBind->name.ids; 
				dbgPrintListEntryNdx < pVarBind->name.idLength; 
				dbgPrintListEntryNdx++, pObjectName++ )
		{
			wsprintf(DbgBuf,"%1d.",*pObjectName);
			OutputDebugString(DbgBuf);
		}
		wsprintf(DbgBuf,"\n");
		OutputDebugString(DbgBuf);
	}
#else
DBG_UNREFERENCED_PARAMETER( pVarBindList );
#endif
}