Figure 3   IChannelHook


 ///////////////////////
 //
 // excerpt from objidl.idl
 //
 //
 
 [
     local,
     object,
     uuid(1008c4a0-7613-11cf-9af1-0020af6e72f4)
 ]
 interface IChannelHook : IUnknown
 {
     // called from GetBuffer prior to sending ORPC response
     void ClientGetSize(
         [in]  REFGUID uExtent,
         [in]  REFIID  riid,
         [out] ULONG  *pDataSize );
 
     // called from GetBuffer to write ORPCTHIS extension
     void ClientFillBuffer(
         [in]      REFGUID uExtent,
         [in]      REFIID  riid,
         [in, out] ULONG  *pDataSize,
         [in]      void   *pDataBuffer );
 
     // called from SendReceive to read ORPCTHAT extension
     void ClientNotify(
         [in] REFGUID uExtent,
         [in] REFIID  riid,
         [in] ULONG   cbDataSize,
         [in] void   *pDataBuffer,
         [in] DWORD   lDataRep,
         [in] HRESULT hrFault );
 
     // called prior to Invoke to read ORPCTHIS extension
     void ServerNotify(
         [in] REFGUID uExtent,
         [in] REFIID  riid,
         [in] ULONG   cbDataSize,
         [in] void   *pDataBuffer,
         [in] DWORD   lDataRep );
 
     // called from server-side GetBuffer
     void ServerGetSize(
         [in]  REFGUID uExtent,
         [in]  REFIID  riid,
         [in]  HRESULT hrFault,
         [out] ULONG  *pDataSize );
 
     // called from server-side GetBuffer to write ORPCTHAT extension
     void ServerFillBuffer(
         [in]      REFGUID uExtent,
         [in]      REFIID  riid,
         [in, out] ULONG  *pDataSize,
         [in]      void   *pDataBuffer,
         [in]      HRESULT hrFault );
 };

Figure 4   SimpleChannelHook

common.h


 ///////////////////////////////////////////////////
 //
 //  common.h - 1997, Don Box
 //  
 //  Common call/thread context structures used by hook
 //
 //
 
 #ifndef _COMMON_H
 #define _COMMON_H
 
 #include <windows.h>
 
 // information about a particular call site
 struct NODE_INFO
 {
     DWORD pid;
     DWORD tid;
     DWORD ip;
 };
 
 // wire representation of ORPCTHIS extension
 struct HOOK_THIS
 {
     GUID  cid;              // fake causality ID
     NODE_INFO niDirect;     // node of immediate caller
     NODE_INFO niIndirect;   // node of original caller
 };
 
 // wire representation of ORPCTHIS extension
 struct HOOK_THAT
 {
     NODE_INFO niTarget;     // node where call executed
 };
 
 // call contexts are a linked list of ORPCTHIS structs
 struct CALL_CONTEXT : public HOOK_THIS
 {
     CALL_CONTEXT *pNext;
 };
 
 // thread context is a linked list of calls + an ORPCTHAT
 struct THREAD_CONTEXT : public HOOK_THAT
 {
     NODE_INFO niThis;
     CALL_CONTEXT *pCurrentCall;
 };
 
 
 // EXTENTID_HostHook is the ORPCTHIS/THAT extension ID
 // {F23ADD53-4992-11d1-991C-006097585A3C}
 DEFINE_GUID(EXTENTID_HostHook, 0xf23add53, 0x4992, 0x11d1, 0x99, 0x1c, 
             0x0, 0x60, 0x97, 0x58, 0x5a, 0x3c);
 
 extern HINSTANCE g_hInstance;
 
 // helper function to grab thread context from TLS
 THREAD_CONTEXT *GetCurrentThreadContext(void);
 
 
 #endif
Hook.h

 ///////////////////////////////////////////////////
 //
 //  Hook.h - 1997, Don Box
 //  
 //  A host info channel hook
 //
 //
 
 #ifndef _HOSTHOOK_H
 #define _HOSTHOOK_H
 
 #include <windows.h>
 
 class HostHook : public IChannelHook
 {
 public:
     STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
     STDMETHODIMP_(ULONG) AddRef(void);
     STDMETHODIMP_(ULONG) Release(void);
 
     STDMETHODIMP_(void) ClientGetSize(REFGUID uExtent, REFIID riid, 
                                       ULONG *pDataSize);
     STDMETHODIMP_(void) ClientFillBuffer(REFGUID uExtent, REFIID riid, 
                                          ULONG *pDataSize, void *pDataBuffer);
     STDMETHODIMP_(void) ClientNotify(REFGUID uExtent, REFIID riid, 
                                      ULONG cbDataSize, void *pDataBuffer, 
                                      DWORD lDataRep, HRESULT hrFault);
 
     STDMETHODIMP_(void) ServerNotify(REFGUID uExtent, REFIID riid, 
                                      ULONG cbDataSize, void *pDataBuffer, 
                                      DWORD lDataRep);
     STDMETHODIMP_(void) ServerGetSize(REFGUID uExtent, REFIID riid, 
                                       HRESULT hrFault, ULONG *pDataSize); 
     STDMETHODIMP_(void) ServerFillBuffer(REFGUID uExtent, REFIID riid, 
                                          ULONG *pDataSize, void *pDataBuffer, 
                                          HRESULT hrFault);
 };
 
 #endif
Hook.cpp

 ///////////////////////////////////////////////////
 //
 //  Hook.cpp - 1997, Don Box
 //  
 //  A host info channel hook
 //
 //
 
 #include "hook.h"
 #include <initguid.h>
 #include "common.h"
 
 #include <assert.h>
 
 
 static NODE_INFO g_niThis;
 DWORD g_dwTLSIndex;
 HANDLE g_hheap;
 HINSTANCE g_hInstance;
 
 BOOL WINAPI DllMain(HINSTANCE h, DWORD dwReason, void *pv) {
  // declare one process-wide channel hook instance
     static HostHook s_theHook;
     switch (dwReason)
     {
     case DLL_PROCESS_ATTACH:
         {
             // turn on sockets if not already enabled in this process
             WSADATA wsad;
             WSAStartup(MAKEWORD(1,1), &wsad);
             // acquire a TLS slot
             if ((g_dwTLSIndex = TlsAlloc()) == 0xFFFFFFFF)
                 return FALSE;
             // register the process-wide channel hook
             HRESULT hr = CoRegisterChannelHook(EXTENTID_HostHook, &s_theHook);
             if (FAILED(hr))
                 return FALSE;
             // initialize various globals including getting the current IP address
             g_hInstance = h;
             g_hheap = HeapCreate(0, 1, 0);
             g_niThis.pid = GetCurrentProcessId();
             char szHostName[1024];
             int res = gethostname(szHostName, 1024);
             if (res == 0) {
                 HOSTENT *p = gethostbyname(szHostName);
                 if (p)
                     memcpy(&g_niThis.ip, p->h_addr, 4);
             }
         }
         break;
     case DLL_THREAD_DETACH:
         {
             // clean up any thread/call context acquired for this thread
             THREAD_CONTEXT *p = (THREAD_CONTEXT *)TlsGetValue(g_dwTLSIndex);
             if (p)
             {
                 CALL_CONTEXT *pc = p->pCurrentCall;
                 while (pc)
                 {
                     p->pCurrentCall = p->pCurrentCall->pNext;
                     HeapFree(g_hheap, 0, pc);
                     pc = p->pCurrentCall;
                 }
                 HeapFree(g_hheap, 0, p);
             }
         }
         break;
     case DLL_PROCESS_DETACH:
         // release TLS slot and deallocate all memory
         if (g_dwTLSIndex != 0xFFFFFFFF)
             TlsFree(g_dwTLSIndex);
         HeapDestroy(g_hheap);
         break;
     }
     return TRUE;
 }
 
 // Helper routine to grab onto the current THREAD_CONTEXT
 THREAD_CONTEXT *GetCurrentThreadContext(void)
 {
     // grab the pointer
     void *pv = TlsGetValue(g_dwTLSIndex);
 
     // if this is the first time through, alloc and init
     if (pv == 0) 
     {
         pv = HeapAlloc(g_hheap, 0, sizeof(THREAD_CONTEXT));
         assert(pv);
         TlsSetValue(g_dwTLSIndex, pv);
         THREAD_CONTEXT& tc = *((THREAD_CONTEXT*)pv);
         tc.pCurrentCall = 0;
         tc.niThis = g_niThis;
         tc.niThis.tid = GetCurrentThreadId();
         tc.niTarget = tc.niThis;
     }
     return (THREAD_CONTEXT*)pv;
 }
 
 // Helper routine to push a new call context onto the current stack
 // based (potentially) on a new incoming call
 CALL_CONTEXT *PushCallContext(void *pvORPCTHIS)
 {
     // get TLS slot for this thread
     THREAD_CONTEXT *pThreadContext = GetCurrentThreadContext();
     assert(pThreadContext);
 
     // allocate a new call context for this call    
     CALL_CONTEXT *pNewCallContext = (CALL_CONTEXT*)HeapAlloc(g_hheap, 0, 
                                                              sizeof(CALL_CONTEXT));
     assert(pNewCallContext);
 
     // initialize new call context
     if (pvORPCTHIS)
     {
         memcpy(static_cast<HOOK_THIS*>(pNewCallContext), pvORPCTHIS, 
                sizeof(HOOK_THIS));
     }
     else if (pThreadContext->pCurrentCall)
     {
         *pNewCallContext = *pThreadContext->pCurrentCall;
         pNewCallContext->niDirect = pThreadContext->niThis;
     }
     else
     {
         CoCreateGuid(&pNewCallContext->cid);
         pNewCallContext->niDirect = pNewCallContext->niIndirect = 
                                     pThreadContext->niThis;
     }
 
     // push context onto stack
     pNewCallContext->pNext = pThreadContext->pCurrentCall;
     pThreadContext->pCurrentCall = pNewCallContext;
     return pNewCallContext;
 }
 
 // Helper routine to pop current call context from the thread stack
 void PopCallContext(void *pvORPCTHAT)
 {
     THREAD_CONTEXT *pThreadContext = GetCurrentThreadContext();
     assert(pThreadContext);
     HOOK_THAT *pht = pThreadContext;
 
     if (pvORPCTHAT)
         *pht = *(HOOK_THAT*)pvORPCTHAT;
     else
         pThreadContext->niTarget = pThreadContext->niThis;
     
     // pop the current call context
     if (pThreadContext->pCurrentCall)
     {
         CALL_CONTEXT *pctx = pThreadContext->pCurrentCall;
         pThreadContext->pCurrentCall = pThreadContext->pCurrentCall->pNext;
         HeapFree(g_hheap, 0, pctx);
     }
 }
 
 STDMETHODIMP HostHook::QueryInterface(REFIID riid, void **ppv)
 {
     if (riid == IID_IUnknown)
         *ppv = static_cast<IUnknown*>(this);
     else if (riid == IID_ChannelHook)
         *ppv = static_cast<IChannelHook*>(this);
     else
         return (*ppv = 0), E_NOINTERFACE;
     reinterpret_cast<IUnknown*>(*ppv)->AddRef();
     return S_OK;
 }
 
 STDMETHODIMP_(ULONG) HostHook::AddRef(void)
 {
     return 2;
 }
 
 STDMETHODIMP_(ULONG) HostHook::Release(void)
 {
     return 1;
 }
 
 
 // called in client prior to making a call
 STDMETHODIMP_(void) HostHook::ClientGetSize(REFGUID uExtent, REFIID riid, 
                                             ULONG *pDataSize)
 {
     assert(uExtent == EXTENTID_HostHook);
     *pDataSize = sizeof(HOOK_THIS);
 }
 
 // called in client prior to making a call
 STDMETHODIMP_(void) HostHook::ClientFillBuffer(REFGUID uExtent, REFIID riid, 
                                                ULONG *pDataSize, 
                                                void *pDataBuffer)
 {
     assert(uExtent == EXTENTID_HostHook);
     CALL_CONTEXT *pContext = PushCallContext(0);
     HOOK_THIS *pORPCTHIS = (HOOK_THIS *)pDataBuffer;
 
     *pORPCTHIS = *pContext;
 
     *pDataSize = sizeof(HOOK_THIS);
 }
 
 // called in client just after a call completes
 STDMETHODIMP_(void) HostHook::ClientNotify(REFGUID uExtent, REFIID riid, 
                                           ULONG cbDataSize, void *pDataBuffer, 
                                            DWORD lDataRep, HRESULT hrFault)
 {
     assert(uExtent == EXTENTID_HostHook);
     assert(lDataRep == NDR_LOCAL_DATA_REPRESENTATION);
     PopCallContext(pDataBuffer);
 }
 
 // called in server just prior to invoking a call
 STDMETHODIMP_(void) HostHook::ServerNotify(REFGUID uExtent, REFIID riid, 
                                            ULONG cbDataSize, void *pDataBuffer, 
                                            DWORD lDataRep)
 {
     assert(uExtent == EXTENTID_HostHook);
     assert(lDataRep == NDR_LOCAL_DATA_REPRESENTATION);
     PushCallContext(pDataBuffer);
 }
 
 // called in server just after invoking a call
 STDMETHODIMP_(void) HostHook::ServerGetSize(REFGUID uExtent, REFIID riid, 
                                            HRESULT hrFault, ULONG *pDataSize)
 {
     *pDataSize = sizeof(HOOK_THAT);
 }
 
 // called in server just after invoking a call
 STDMETHODIMP_(void) HostHook::ServerFillBuffer(REFGUID uExtent, REFIID riid, 
                                               ULONG *pDataSize, 
                                               void *pDataBuffer, HRESULT hrFault)
 {
     assert(uExtent == EXTENTID_HostHook);
     THREAD_CONTEXT *pThreadContext = GetCurrentThreadContext();
     HOOK_THAT *pORPCTHAT = (HOOK_THAT *)pDataBuffer;
 
     pORPCTHAT->niTarget = pThreadContext->niThis;
     
     *pDataSize = sizeof(HOOK_THAT);
     PopCallContext(0);
 }

Figure 5   CallInfo.idl


 interface ICallInfo : IUnknown {
   [propget] HRESULT OriginalProcessID([out, retval] long *pVal);
   [propget] HRESULT OriginalThreadID([out, retval] long *pVal);
   [propget] HRESULT OriginalHostID([out, retval] long *pVal);
   [propget] HRESULT OriginalHostName([out, retval] BSTR *pVal);
 
   [propget] HRESULT DirectProcessID([out, retval] long *pVal);
   [propget] HRESULT DirectThreadID([out, retval] long *pVal);
   [propget] HRESULT DirectHostID([out, retval] long *pVal);
   [propget] HRESULT DirectHostName([out, retval] BSTR *pVal);
 
   [propget] HRESULT PseudoCausalityID([out, retval] GUID *pguid);
 }

Figure 6   CallInfo

callinfo.h


 ///////////////////////////////////////////////////
 //
 //  callinfo.h - 1997, Don Box
 //  
 //  External accessor to host info stored by channel hook
 //
 //
 
 
 #ifndef _CALLINFO_H
 #define _CALLINFO_H
 
 
 #include <windows.h>
 #include "hosthook.h"
 
 class CallInfo : public ICallInfo, public IClientCallInfo
 {
 public:
     STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
     STDMETHODIMP_(ULONG) AddRef(void);
     STDMETHODIMP_(ULONG) Release(void);
 
     STDMETHODIMP get_OriginalProcessID(long *pVal);
     STDMETHODIMP get_OriginalThreadID(long *pVal);
     STDMETHODIMP get_OriginalHostID(long *pVal);
     STDMETHODIMP get_OriginalHostName(BSTR *pVal);
 
     STDMETHODIMP get_DirectProcessID(long *pVal);
     STDMETHODIMP get_DirectThreadID(long *pVal);
     STDMETHODIMP get_DirectHostID(long *pVal);
     STDMETHODIMP get_DirectHostName(BSTR *pVal);
 
     STDMETHODIMP get_PseudoCausalityID(GUID *pguid);
 
     STDMETHODIMP get_TargetProcessID(long *pVal);
     STDMETHODIMP get_TargetThreadID(long *pVal);
     STDMETHODIMP get_TargetHostID(long *pVal);
     STDMETHODIMP get_TargetHostName(BSTR *pVal);
 };
 
 #endif
callinfo.cpp

 ///////////////////////////////////////////////////
 //
 //  callinfo.cpp - 1997, Don Box
 //  
 //  External accessor to host info stored by channel hook
 //
 //
 
 #include "callinfo.h"
 
 #include "common.h"
 
 #include "hosthook_i.c"
 
 // helper routine to convert IP address to a BSTR
 BSTR GetNameFromIPAddress(DWORD dwIP)
 {
     HOSTENT *p = gethostbyaddr((const char*)&dwIP, 4, AF_INET);
     if (p && p->h_name)
     {
         int cch = lstrlenA(p->h_name);
         BSTR bstr = SysAllocStringLen(0, cch);
         if (bstr)
             MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, p->h_name, cch + 1,          
	                      bstr, cch + 1);
         return bstr;
     }
     return 0;
 }
 
 STDMETHODIMP CallInfo::QueryInterface(REFIID riid, void **ppv)
 {
     if (riid == IID_IUnknown)
         *ppv = static_cast<ICallInfo*>(this);
     else if (riid == IID_ICallInfo)
         *ppv = static_cast<ICallInfo*>(this);
     else if (riid == IID_IClientCallInfo)
         *ppv = static_cast<IClientCallInfo*>(this);
     else
         return (*ppv = 0), E_NOINTERFACE;
     reinterpret_cast<IUnknown*>(*ppv)->AddRef();
     return S_OK;
 
 }
 
 STDMETHODIMP_(ULONG) CallInfo::AddRef(void)
 {
     return 2;
 }
 
 STDMETHODIMP_(ULONG) CallInfo::Release(void)
 {
     return 1;
 }
 
 STDMETHODIMP CallInfo::get_OriginalProcessID(long *pVal)
 {
     THREAD_CONTEXT *pctx = GetCurrentThreadContext();
     NODE_INFO& rni = pctx->pCurrentCall ? pctx->pCurrentCall->niIndirect : 
                                           pctx->niThis;
     *pVal = rni.pid;
     return S_OK;
 }
 
 STDMETHODIMP CallInfo::get_OriginalThreadID(long *pVal)
 {
     THREAD_CONTEXT *pctx = GetCurrentThreadContext();
     NODE_INFO& rni = pctx->pCurrentCall ? pctx->pCurrentCall->niIndirect : 
                                           pctx->niThis;
     *pVal = rni.tid;
     return S_OK;
 }
 
 STDMETHODIMP CallInfo::get_OriginalHostID(long *pVal)
 {
     THREAD_CONTEXT *pctx = GetCurrentThreadContext();
     NODE_INFO& rni = pctx->pCurrentCall ? pctx->pCurrentCall->niIndirect : 
                                           pctx->niThis;
     *pVal = rni.ip;
     return S_OK;
 }
 
 STDMETHODIMP CallInfo::get_OriginalHostName(BSTR *pVal)
 {
     THREAD_CONTEXT *pctx = GetCurrentThreadContext();
     NODE_INFO& rni = pctx->pCurrentCall ? pctx->pCurrentCall->niIndirect : 
                                           pctx->niThis;
     *pVal = GetNameFromIPAddress(rni.ip);
     return S_OK;
 }
 
 STDMETHODIMP CallInfo::get_DirectProcessID(long *pVal)
 {
     THREAD_CONTEXT *pctx = GetCurrentThreadContext();
     NODE_INFO& rni = pctx->pCurrentCall ? pctx->pCurrentCall->niDirect : 
                                           pctx->niThis;
     *pVal = rni.pid;
     return S_OK;
 }
 
 STDMETHODIMP CallInfo::get_DirectThreadID(long *pVal)
 {
     THREAD_CONTEXT *pctx = GetCurrentThreadContext();
     NODE_INFO& rni = pctx->pCurrentCall ? pctx->pCurrentCall->niDirect : 
                                           pctx->niThis;
     *pVal = rni.tid;
     return S_OK;
 }
 
 STDMETHODIMP CallInfo::get_DirectHostID(long *pVal)
 {
     THREAD_CONTEXT *pctx = GetCurrentThreadContext();
     NODE_INFO& rni = pctx->pCurrentCall ? pctx->pCurrentCall->niDirect : 
                                          pctx->niThis;
     *pVal = rni.ip;
     return S_OK;
 }
 
 STDMETHODIMP CallInfo::get_DirectHostName(BSTR *pVal)
 {
     THREAD_CONTEXT *pctx = GetCurrentThreadContext();
     NODE_INFO& rni = pctx->pCurrentCall ? pctx->pCurrentCall->niDirect : 
                                          pctx->niThis;
     *pVal = GetNameFromIPAddress(rni.ip);
     return S_OK;
 }
 
 STDMETHODIMP CallInfo::get_PseudoCausalityID(GUID *pguid)
 {
     THREAD_CONTEXT *pctx = GetCurrentThreadContext();
     if (pctx->pCurrentCall)
         *pguid = pctx->pCurrentCall->cid;
     else
         *pguid = GUID_NULL;
     return S_OK;
 }
 
 STDMETHODIMP CallInfo::get_TargetProcessID(long *pVal)
 {
     THREAD_CONTEXT *pctx = GetCurrentThreadContext();
     NODE_INFO& rni = pctx->niTarget;
     *pVal = rni.pid;
     return S_OK;
 }
 
 STDMETHODIMP CallInfo::get_TargetThreadID(long *pVal)
 {
     THREAD_CONTEXT *pctx = GetCurrentThreadContext();
     NODE_INFO& rni = pctx->niTarget;
     *pVal = rni.tid;
     return S_OK;
 }
 
 STDMETHODIMP CallInfo::get_TargetHostID(long *pVal)
 {
     THREAD_CONTEXT *pctx = GetCurrentThreadContext();
     NODE_INFO& rni = pctx->niTarget;
     *pVal = rni.ip;
     return S_OK;
 }
 
 STDMETHODIMP CallInfo::get_TargetHostName(BSTR *pVal)
 {
     THREAD_CONTEXT *pctx = GetCurrentThreadContext();
     NODE_INFO& rni = pctx->niTarget;
     *pVal = GetNameFromIPAddress(rni.ip);
     return S_OK;
 }