/***************************************************************************** ****************************************************************************** ** ** ** RAICShelp.c ** Contains the useful public entry points to an ICS-assistance library ** created for the Salem/PCHealth Remote Assistance feature in Whistler ** ** Dates: ** 11-1-2000 created by TomFr ** 11-17-2000 re-written as a DLL, had been an object. ** 2-15-20001 Changed to a static lib, support added for dpnathlp.dll ** 5-2-2001 Support added for dpnhupnp.dll & dpnhpast.dll ** ****************************************************************************** *****************************************************************************/ #define INIT_GUID #include #include #include #include #include #include #include #include #include #include "ICSutils.h" #include "rsip.h" #include "icshelpapi.h" #include #include #include #include #include #include #include /***************************************************************************** ** Some global variables *****************************************************************************/ // the mark of the beast... #define NO_ICS_HANDLE 0x666 long g_waitDuration=120000; BOOL g_boolIcsPresent = FALSE; BOOL g_boolIcsOnThisMachine = FALSE; BOOL g_boolIcsFound = FALSE; BOOL g_boolUsingNatHelp = FALSE; BOOL g_boolUsingNatPAST = FALSE; BOOL g_boolInitialized = FALSE; SOCKADDR_IN g_saddrLocal; HANDLE g_hWorkerThread = 0; HMODULE g_hModDpNatHlp = NULL; PDIRECTPLAYNATHELP g_pDPNH = NULL; char g_szPublicAddr[45]; char *g_lpszDllName = "NULL"; char szInternal[]="internal"; typedef struct _MAPHANDLES { int iMapped; DPNHHANDLE hMapped[16]; } MAPHANDLES, *PMAPHANDLES; #define DP_NAT_HANDLES 256 PMAPHANDLES g_PortHandles[DP_NAT_HANDLES]; int iDbgFileHandle; typedef struct _SUPDLLS { char *szDllName; BOOL bUsesUpnp; // TRUE if we ICS supports UPnP } SUPDLLS, *PSUPDLLS; SUPDLLS strDpHelp[] = { {"dpnhupnp.dll", TRUE}, {"dpnhpast.dll", FALSE}, {NULL, FALSE} }; /******* USEFULL STUFF **********/ #ifndef ARRAYSIZE #define ARRAYSIZE(x) sizeof(x)/sizeof(x[0]) #endif // forward declares... int GetTsPort(void); /**************************************************************************** ** ** DumpLibHr- ** Gives us debug spew for the HRESULTS coming back from DPNATHLP.DLL ** ****************************************************************************/ void DumpLibHr(HRESULT hr) { char *pErr = NULL; char scr[400]; switch (hr){ case DPNH_OK: pErr = "DPNH_OK"; break; case DPNHSUCCESS_ADDRESSESCHANGED: pErr = "DPNHSUCCESS_ADDRESSESCHANGED"; break; case DPNHERR_ALREADYINITIALIZED: pErr = "DPNHERR_ALREADYINITIALIZED"; break; case DPNHERR_BUFFERTOOSMALL: pErr = "DPNHERR_BUFFERTOOSMALL"; break; case DPNHERR_GENERIC: pErr = "DPNHERR_GENERIC"; break; case DPNHERR_INVALIDFLAGS: pErr = "DPNHERR_INVALIDFLAGS"; break; case DPNHERR_INVALIDOBJECT: pErr = "DPNHERR_INVALIDOBJECT"; break; case DPNHERR_INVALIDPARAM: pErr = "DPNHERR_INVALIDPARAM"; break; case DPNHERR_INVALIDPOINTER: pErr = "DPNHERR_INVALIDPOINTER"; break; case DPNHERR_NOMAPPING: pErr = "DPNHERR_NOMAPPING"; break; case DPNHERR_NOMAPPINGBUTPRIVATE: pErr = "DPNHERR_NOMAPPINGBUTPRIVATE"; break; case DPNHERR_NOTINITIALIZED: pErr = "DPNHERR_NOTINITIALIZED"; break; case DPNHERR_OUTOFMEMORY: pErr = "DPNHERR_OUTOFMEMORY"; break; case DPNHERR_PORTALREADYREGISTERED: pErr = "DPNHERR_PORTALREADYREGISTERED"; break; case DPNHERR_PORTUNAVAILABLE: pErr = "DPNHERR_PORTUNAVAILABLE"; break; case DPNHERR_SERVERNOTAVAILABLE: pErr = "DPNHERR_SERVERNOTAVAILABLE"; break; case DPNHERR_UPDATESERVERSTATUS: pErr = "DPNHERR_UPDATESERVERSTATUS"; break; default: wsprintfA(scr, "unknown error: 0x%x", hr); pErr = scr; break; }; IMPORTANT_MSG((L"DpNatHlp result=%S", pErr)); } /**************************************************************************** ** ** GetAllAdapters ** ****************************************************************************/ int GetAllAdapters(int *iFound, int iMax, SOCKADDR_IN *sktArray) { PIP_ADAPTER_INFO p; PIP_ADDR_STRING ps; DWORD dw; ULONG ulSize = 0; int i=0; PIP_ADAPTER_INFO pAdpInfo = NULL; if (!iFound || !sktArray) return 1; *iFound = 0; ZeroMemory(sktArray, sizeof(SOCKADDR) * iMax); dw = GetAdaptersInfo( pAdpInfo, &ulSize ); pAdpInfo = (IP_ADAPTER_INFO*)malloc(ulSize); if (!pAdpInfo) { INTERESTING_MSG((L"GetAddr malloc failed")); return 1; } dw = GetAdaptersInfo( pAdpInfo, &ulSize); if (dw != ERROR_SUCCESS) { INTERESTING_MSG((L"GetAdaptersInfo failed")); free(pAdpInfo); return 1; } for(p=pAdpInfo; p!=NULL; p=p->Next) { for(ps = &(p->IpAddressList); ps; ps=ps->Next) { if (strcmp(ps->IpAddress.String, "0.0.0.0") != 0 && i < iMax) { sktArray[i].sin_family = AF_INET; sktArray[i].sin_addr.S_un.S_addr = inet_addr(ps->IpAddress.String); TRIVIAL_MSG((L"Found adapter #%d at [%S]", i+1, ps->IpAddress.String)); i++; } } } *iFound = i; TRIVIAL_MSG((L"GetAllAdapters- %d found", *iFound)); free(pAdpInfo); return 0; } /**************************************************************************** ** ** OpenPort(int port) ** if there is no ICS available, then we should just return... ** ** Of course, we save away the Port, as it goes back in the ** FetchAllAddresses call, asthe formatted "port" whenever a ** different one is not specified. ** ****************************************************************************/ DWORD APIENTRY OpenPort(int Port) { DWORD dwRet = (int)-1; TRIVIAL_MSG((L"OpenPort(%d)", Port )); if (!g_boolInitialized) { HEINOUS_E_MSG((L"ERROR: OpenPort- library not initialized")); return 0; } // save away for later retrieval g_iPort = Port; if (g_boolIcsPresent && g_pDPNH) { HRESULT hr=0; int i; DPNHHANDLE *pHnd; SOCKADDR_IN lSockAddr[16]; PMAPHANDLES hMap; for (i=0;g_PortHandles[i] != NULL; i++); if (i >= ARRAYSIZE(g_PortHandles)) { // we have no more memory for mappings! // should never hit this, unless we are leaking... HEINOUS_E_MSG((L"Out of table space in OpenPort")); return 0; } // now we have a pointer for our handle array hMap = (PMAPHANDLES)malloc(sizeof(MAPHANDLES)); g_PortHandles[i] = hMap; dwRet = 0x8000000 + i; // get adapters GetAllAdapters(&hMap->iMapped, ARRAYSIZE(lSockAddr), &lSockAddr[0]); TRIVIAL_MSG((L"GetAllAdapters found %d adapters to deal with", hMap->iMapped)); /* Now we cycle through all the found adapters and get a mapping for each * This insures that the ICF is opened on all adapters... */ for (i = 0; i < hMap->iMapped; i++) { pHnd = &hMap->hMapped[i]; lSockAddr[i].sin_port = ntohs((unsigned)Port); hr = IDirectPlayNATHelp_RegisterPorts(g_pDPNH, (SOCKADDR *)&lSockAddr[i], sizeof(lSockAddr[0]), 1, 30000, pHnd, DPNHREGISTERPORTS_TCP); if (hr != DPNH_OK) { IMPORTANT_MSG((L"RegisterPorts failed in OpenPort for adapter #%d, ", i )); DumpLibHr(hr); } else { TRIVIAL_MSG((L"OpenPort Assigned: 0x%x", *pHnd)); } } } else { dwRet = NO_ICS_HANDLE; TRIVIAL_MSG((L"OpenPort- no ICS found")); } TRIVIAL_MSG((L"OpenPort- returns 0x%x", dwRet )); return dwRet; } /**************************************************************************** ** ** Called to close a port, whenever a ticket is expired or closed. ** ****************************************************************************/ DWORD APIENTRY ClosePort(DWORD MapHandle) { DWORD dwRet = ERROR_SUCCESS; DWORD dwIndex; TRIVIAL_MSG((L"ClosePort(0x%x)", MapHandle )); if (!g_boolInitialized) { HEINOUS_E_MSG((L"ERROR: ClosePort- library not initialized")); return ERROR_INVALID_PARAMETER; } // if we didn't open this thru the ICS, then just return if (!g_boolIcsPresent && MapHandle == NO_ICS_HANDLE) return ERROR_SUCCESS; dwIndex = MapHandle - 0x8000000; if (g_boolIcsPresent && g_pDPNH && dwIndex < ARRAYSIZE(g_PortHandles)) { HRESULT hr=0; int i; PMAPHANDLES pMap = g_PortHandles[dwIndex]; if (pMap) { TRIVIAL_MSG((L"closing %d port mappings", pMap->iMapped)); for (i = 0; i < pMap->iMapped; i++) { hr = IDirectPlayNATHelp_DeregisterPorts(g_pDPNH, pMap->hMapped[i], 0); if (hr != DPNH_OK) { IMPORTANT_MSG((L"DeregisterPorts failed in ClosePort for handle 0x%x", pMap->hMapped[i])); DumpLibHr(hr); dwRet = ERROR_INVALID_ACCESS; } } // remove the handle from our array free(g_PortHandles[dwIndex]); g_PortHandles[dwIndex] = NULL; } } else IMPORTANT_MSG((L"Bad handle passed into ClosePort!!")); TRIVIAL_MSG((L"ClosePort returning 0x%x", dwRet )); return(dwRet); } /**************************************************************************** ** ** FetchAllAddresses ** Returns a string listing all the valid IP addresses for the machine ** Formatting details: ** 1. Each address is seperated with a ";" (semicolon) ** 2. Each address consists of the "1.2.3.4", and is followed by ":p" ** where the colon is followed by the port number ** ****************************************************************************/ DWORD APIENTRY FetchAllAddresses(WCHAR *lpszAddr, int iBufSize) { return FetchAllAddressesEx(lpszAddr, iBufSize, IPF_ADD_DNS); } /**************************************************************************** ** ** CloseAllPorts ** Does just that- closes all port mappings that have been opened ** ****************************************************************************/ DWORD APIENTRY CloseAllOpenPorts(void) { DWORD dwRet = 1; INTERESTING_MSG((L"CloseAllOpenPorts()" )); if (g_boolIcsPresent && g_pDPNH) { HRESULT hr=0; int i; // call DPNATHLP to unregister the mapping // then remove the handle from our array for (i = 0; i < ARRAYSIZE(g_PortHandles); i++) { if (g_PortHandles[i]) { PMAPHANDLES pMap = g_PortHandles[i]; int j; for (j = 0; j < pMap->iMapped; j++) { hr = IDirectPlayNATHelp_DeregisterPorts(g_pDPNH, pMap->hMapped[j], 0); if (hr != DPNH_OK) { IMPORTANT_MSG((L"DeregisterPorts failed in CloseAllOpenPorts")); DumpLibHr(hr); } } free(g_PortHandles[i]); g_PortHandles[i] = 0; } } } return(dwRet); } /**************************************************************************** ** ** The worker thread for use with the DPHATHLP.DLL. ** ** This keeps the leases updated on any open ** port assignments. Eventually, this will also check & update the sessmgr ** when the ICS comes & goes, or when the address list changes. ** ****************************************************************************/ DWORD WINAPI DpNatHlpThread(PVOID ContextPtr) { DWORD dwRet=1; DWORD dwWaitResult=WAIT_TIMEOUT; ULONG ulCurIpTblSz, ulLastIpTblSz; long l_waitTime = g_waitDuration; PMIB_IPADDRTABLE pCurIpTbl=NULL, pLastIpTbl=NULL; TRIVIAL_MSG((L"DpNatHlpThread()" )); /* * Initialize the IP tables that we keep * Get 1 copy from IP Helper, then make a * second local copy for later comparison. */ ulLastIpTblSz=0; GetIpAddrTable(NULL, &ulLastIpTblSz, FALSE); pLastIpTbl = (PMIB_IPADDRTABLE)malloc(ulLastIpTblSz); if (!pLastIpTbl) goto shutdown; if (NO_ERROR != GetIpAddrTable(pLastIpTbl, &ulLastIpTblSz, FALSE)) IMPORTANT_MSG((L"Failed to get initial IpAddrTable in DpNatHlpThread; err=0x%x", GetLastError())); TRIVIAL_MSG((L"Initial IPtblSz = %d", ulLastIpTblSz)); ulCurIpTblSz=ulLastIpTblSz; pCurIpTbl = (PMIB_IPADDRTABLE)malloc(ulCurIpTblSz); if (!pCurIpTbl) { goto shutdown; } memcpy(pCurIpTbl, pLastIpTbl, ulCurIpTblSz); /* * Then the 2 minute wait loop */ while(dwWaitResult == WAIT_TIMEOUT) { DWORD dwTime; BOOL bAddrChanged; if (dwWaitResult != WAIT_TIMEOUT) { IMPORTANT_MSG((L"error: got bad wait return in DpNatHlpThread() val=0x%x", dwWaitResult)); goto shutdown; } // our default is NO CHANGE bAddrChanged = FALSE; if (NO_ERROR != GetIpAddrTable(pCurIpTbl, &ulCurIpTblSz, FALSE)) { /* * If we couldn't get the tables, it would be because the table grew * in size beyind the old one. Hmm- that must mean that the table changed */ bAddrChanged=TRUE; TRIVIAL_MSG((L"IpAddrTable GREW in DpNatHlpThread")); } else { /* * If the table shrunk, then it must have changed */ if (ulCurIpTblSz != ulLastIpTblSz) { bAddrChanged = TRUE; TRIVIAL_MSG((L"IpAddrTable SHRUNK in DpNatHlpThread")); } /* * Maybe one of the addresses in the table changed? */ if (0 != memcmp(pCurIpTbl, pLastIpTbl, ulCurIpTblSz)) { bAddrChanged=TRUE; TRIVIAL_MSG((L"IpAddrTable CHANGED in DpNatHlpThread")); } } if (bAddrChanged) { TRIVIAL_MSG((L"DpNatHlpThread: IP Tables changed; cur=%d, last=%d", ulCurIpTblSz, ulLastIpTblSz)); /* free the old tables */ if (pCurIpTbl) free(pCurIpTbl); if (pLastIpTbl) free(pLastIpTbl); /* then get new tables */ ulLastIpTblSz=0; GetIpAddrTable(NULL, &ulLastIpTblSz, FALSE); pLastIpTbl = (PMIB_IPADDRTABLE)malloc(ulLastIpTblSz); if(pLastIpTbl) { if (NO_ERROR != GetIpAddrTable(pLastIpTbl, &ulLastIpTblSz, FALSE)) IMPORTANT_MSG((L"Failed to update IpAddrTable in DpNatHlpThread; err=0x%x", GetLastError())); ulCurIpTblSz=ulLastIpTblSz; pCurIpTbl = (PMIB_IPADDRTABLE)malloc(ulCurIpTblSz); memcpy(pCurIpTbl, pLastIpTbl, ulCurIpTblSz); } else { /* we got here because the machine ran out of memory * so we will try again later to allocate the tables * maybe more memory will be free then. */ pCurIpTbl = NULL; pLastIpTbl = NULL; ulLastIpTblSz=0; ulCurIpTblSz=1; IMPORTANT_MSG((L"Failed to get memory for IP tables in DpNatHlpThread; err=0x%x", GetLastError())); } } if (g_pDPNH) { HRESULT hr; DPNHCAPS lCaps; /* Call GetCaps to renew all open leases */ lCaps.dwSize = sizeof(lCaps); hr = IDirectPlayNATHelp_GetCaps(g_pDPNH, &lCaps, DPNHGETCAPS_UPDATESERVERSTATUS); if (hr == DPNH_OK) { if (lCaps.dwMinLeaseTimeRemaining) l_waitTime = min(g_waitDuration, (long)lCaps.dwMinLeaseTimeRemaining); } else if (hr == DPNHSUCCESS_ADDRESSESCHANGED) bAddrChanged = TRUE; else { IMPORTANT_MSG((L"GetCaps failed in NatHlpDaemon")); DumpLibHr(hr); } } if (bAddrChanged && g_hAlertEvent) { SetEvent(g_hAlertEvent); } dwWaitResult = WaitForSingleObject(g_hThreadEvent, l_waitTime); } shutdown: TRIVIAL_MSG((L"DpNatHlpThread shutting down")); /* * Then the shutdown code * free all memory * then close out DPNATHLP.DLL * and return all objects */ if (pCurIpTbl) free(pCurIpTbl); if (pLastIpTbl) free(pLastIpTbl); if (g_pDPNH) { IDirectPlayNATHelp_Close(g_pDPNH, 0); IDirectPlayNATHelp_Release(g_pDPNH); } g_pDPNH = NULL; if (g_hModDpNatHlp) FreeLibrary(g_hModDpNatHlp); g_hModDpNatHlp = 0; DeleteCriticalSection( &g_CritSec ); CloseHandle(g_hThreadEvent); TRIVIAL_MSG((L"DpNatHlpThread() returning 0x%x", dwRet )); WSACleanup(); ExitThread(dwRet); // of course we never get this far... return(dwRet); } /**************************************************************************** ** ** The actual worker thread. This keeps the leases updated on any open ** port assignments. This will also check & update the sessmgr ** when the ICS comes & goes, or when the address list changes. ** ****************************************************************************/ DWORD WINAPI RsipDaemonThread(PVOID ContextPtr) { DWORD dwRet=1; DWORD dwWaitResult=WAIT_TIMEOUT; ULONG ulCurIpTblSz, ulLastIpTblSz; PMIB_IPADDRTABLE pCurIpTbl=NULL, pLastIpTbl=NULL; TRIVIAL_MSG((L"RsipDaemonThread()" )); /* * Initialize the IP tables that we keep * Get 1 copy from IP Helper, then make a * second local copy for later comparison. */ ulLastIpTblSz=0; GetIpAddrTable(NULL, &ulLastIpTblSz, FALSE); pLastIpTbl = (PMIB_IPADDRTABLE)malloc(ulLastIpTblSz); if (NO_ERROR != GetIpAddrTable(pLastIpTbl, &ulLastIpTblSz, FALSE)) IMPORTANT_MSG((L"Failed to get initial IpAddrTable in RsipDaemonThread; err=0x%x", GetLastError())); TRIVIAL_MSG((L"Initial IPtblSz = %d", ulLastIpTblSz)); ulCurIpTblSz=ulLastIpTblSz; pCurIpTbl = (PMIB_IPADDRTABLE)malloc(ulCurIpTblSz); if (!pCurIpTbl) goto shutdown; memcpy(pCurIpTbl, pLastIpTbl, ulCurIpTblSz); /* * Then the 2 minute wait loop */ while(dwWaitResult == WAIT_TIMEOUT) { DWORD dwTime; BOOL bAddrChanged; if (dwWaitResult != WAIT_TIMEOUT) { IMPORTANT_MSG((L"error: got bad wait return in RsipDaemonThread() val=0x%x", dwWaitResult)); goto shutdown; } // our default is NO CHANGE bAddrChanged = FALSE; if (NO_ERROR != GetIpAddrTable(pCurIpTbl, &ulCurIpTblSz, FALSE)) { /* * If we couldn't get the tables, it would be because the table grew * in size beyind the old one. Hmm- that must mean that the table changed */ bAddrChanged=TRUE; TRIVIAL_MSG((L"IpAddrTable GREW in RsipDaemonThread")); } else { /* * If the table shrunk, then it must have changed */ if (ulCurIpTblSz != ulLastIpTblSz) { bAddrChanged = TRUE; TRIVIAL_MSG((L"IpAddrTable SHRUNK in RsipDaemonThread")); } /* * Maybe one of the addresses in the table changed? */ if (0 != memcmp(pCurIpTbl, pLastIpTbl, ulCurIpTblSz)) { bAddrChanged=TRUE; TRIVIAL_MSG((L"IpAddrTable CHANGED in RsipDaemonThread")); } } if (bAddrChanged) { TRIVIAL_MSG((L"RsipDaemonThread: IP Tables changed; cur=%d, last=%d", ulCurIpTblSz, ulLastIpTblSz)); /* free the old tables */ if (pCurIpTbl) free(pCurIpTbl); if (pLastIpTbl) free(pLastIpTbl); /* then get new tables */ ulLastIpTblSz=0; GetIpAddrTable(NULL, &ulLastIpTblSz, FALSE); pLastIpTbl = (PMIB_IPADDRTABLE)malloc(ulLastIpTblSz); if(pLastIpTbl) { if (NO_ERROR != GetIpAddrTable(pLastIpTbl, &ulLastIpTblSz, FALSE)) IMPORTANT_MSG((L"Failed to update IpAddrTable in RsipDaemonThread; err=0x%x", GetLastError())); ulCurIpTblSz=ulLastIpTblSz; pCurIpTbl = (PMIB_IPADDRTABLE)malloc(ulCurIpTblSz); memcpy(pCurIpTbl, pLastIpTbl, ulCurIpTblSz); } else { /* we got here because the machine ran out of memory * so we will try again later to allocate the tables * maybe more memory will be free then. */ pCurIpTbl = NULL; pLastIpTbl = NULL; ulLastIpTblSz=0; ulCurIpTblSz=1; IMPORTANT_MSG((L"Failed to get memory for IP tables in RsipDaemonThread; err=0x%x", GetLastError())); } } if (g_boolIcsPresent) { PRSIP_LEASE_RECORD pLeaseRecord; // update the leases dwTime = timeGetTime(); if (!PortExtend( dwTime ) && g_hAlertEvent) { // If we cannot extend the lease, then we must notify the user TRIVIAL_MSG((L"Sending RSIP alert event from RsipDaemonThread")); bAddrChanged = TRUE; } CacheClear( dwTime ); if (g_iPort && (pLeaseRecord = FindLease( TRUE, htons((WORD)g_iPort) ))) { /* . * We got here because there are open leases. That means we must * periodically check to see if the public address has changed. * This would happen if the ICS's modem got disconnected or * reconnected. In the first case, we no longer have a valid public * address, in the second case we have a new public address. */ SOCKADDR_IN saddrPublic, saddrNew; DWORD dwBindId = 0; DWORD dwSpewSave; saddrPublic.sin_family = AF_INET; saddrPublic.sin_addr.s_addr = pLeaseRecord->addrV4; saddrPublic.sin_port = pLeaseRecord->rport; dwSpewSave = gDbgFlag; gDbgFlag |= 3; /* * The basic algorithm for this is to ask for a new port mapping * and compare the address to what we already have. I chose to ask * for a port that will never be mapped onto a MS PC- the Sun * RPC port. This should always be available to us, as no * smart gamer will use this port. * It should not cause us any security problems, as the OS never * has any sockets open & listening on that port. Besides, the * port mapping is only valid for ~500 mSec- not long enough to cause * any real mischief. * BTW, this algorithm workd on WinME ICS servers as well. */ if (S_OK == AssignPort( TRUE, htons(111), (SOCKADDR *)&saddrNew, &dwBindId )) { // free the port mapping, as all we care about is the public address FreePort(dwBindId); gDbgFlag = dwSpewSave; // check for invalid address (modem probably disconnected) if (!saddrNew.sin_addr.s_addr) { TRIVIAL_MSG((L"Public address all zeros- no longer on Internet")); /* * How should this be handled? * Well, if we notify Salem, then they will try to update the ticket. * but that may cause more trouble, as that would cause a reconnect * which would then cause another address change, which would cause * a second ticket update. * * We certainly should try to maintain the current leases, in case * the connection returns. */ // bAddrChanged=TRUE; } else if (saddrNew.sin_addr.s_addr != saddrPublic.sin_addr.s_addr) { // change the public address in all the leases... RSIP_LEASE_RECORD *pLeaseWalker; RSIP_LEASE_RECORD *pNextLease; TRIVIAL_MSG((L"==> public address changed: OLD, then NEW address <==")); DumpSocketAddress( 8, (SOCKADDR *)&saddrPublic, AF_INET ); DumpSocketAddress( 8, (SOCKADDR *)&saddrNew, AF_INET ); pLeaseWalker = g_pRsipLeaseRecords; while( pLeaseWalker ) { pNextLease = pLeaseWalker->pNext; // copy the address pLeaseWalker->addrV4 = saddrNew.sin_addr.s_addr; pLeaseWalker=pNextLease; } bAddrChanged=TRUE; } } else { gDbgFlag = dwSpewSave; IMPORTANT_MSG((L"AssignPort failed in daemon thread")); // Hmmm- we can't get to the ICS server any longer bAddrChanged=TRUE; // delete all open mappings CloseAllOpenPorts(); } } } if (bAddrChanged && g_hAlertEvent) { SetEvent(g_hAlertEvent); } dwWaitResult = WaitForSingleObject(g_hThreadEvent, g_waitDuration); } shutdown: TRIVIAL_MSG((L"RsipDaemonThread shutting down")); /* * Then the shutdown code * release any open ports */ if (pCurIpTbl) free(pCurIpTbl); if (pLastIpTbl) free(pLastIpTbl); Deinitialize(); DeleteCriticalSection( &g_CritSec ); CloseHandle(g_hThreadEvent); TRIVIAL_MSG((L"RsipDaemonThread() returning 0x%x", dwRet )); WSACleanup(); ExitThread(dwRet); // of course we never get this far... return(dwRet); } /**************************************************************************** ** ** This should initialize the ICS library for use with the DirectPlay ** ICS/NAT helper DLL. ** All will happen in this order: ** 1. Call DirectPlayNATHelpCreate() ** 2. Call IDirectPlayNATHelp::Initialize to prepare the object ** ****************************************************************************/ DWORD StartDpNatHlp(void) { DWORD dwRet = ERROR_CALL_NOT_IMPLEMENTED; HRESULT hr; HMODULE hMod; PFN_DIRECTPLAYNATHELPCREATE pfnDirectPlayNATHelpCreate; PDIRECTPLAYNATHELP pDirectPlayNATHelp = NULL; DPNHCAPS dpnhCaps; PSUPDLLS pDll = &strDpHelp[0]; try_again: TRIVIAL_MSG((L"starting StartDpNatHlp for %S (%S)", pDll->szDllName, pDll->bUsesUpnp?"UPnP":"PAST")); // start out with no public address g_szPublicAddr[0] = 0; hMod = LoadLibraryA(pDll->szDllName); if (!hMod) { IMPORTANT_MSG((L"ERROR:%S could not be found", pDll->szDllName)); pDll++; if (!pDll->szDllName) { dwRet = ERROR_FILE_NOT_FOUND; goto done; } goto try_again; } pfnDirectPlayNATHelpCreate = (PFN_DIRECTPLAYNATHELPCREATE) GetProcAddress(hMod, "DirectPlayNATHelpCreate"); if (!pfnDirectPlayNATHelpCreate) { IMPORTANT_MSG((L"\"DirectPlayNATHelpCreate\" proc in %S could not be found", pDll->szDllName)); dwRet = ERROR_INVALID_FUNCTION; goto done; } hr = pfnDirectPlayNATHelpCreate(&IID_IDirectPlayNATHelp, (void**) (&pDirectPlayNATHelp)); if (hr != DPNH_OK) { IMPORTANT_MSG((L"DirectPlayNATHelpCreate failed in %S", pDll->szDllName)); DumpLibHr(hr); dwRet = ERROR_BAD_UNIT; goto done; } hr = IDirectPlayNATHelp_Initialize(pDirectPlayNATHelp, 0); if (hr != DPNH_OK) { IMPORTANT_MSG((L"IDirectPlayNATHelp_Initialize failed in %S", pDll->szDllName)); DumpLibHr(hr); dwRet = ERROR_BAD_UNIT; goto done; } /* Get capabilities of NAT/RSIP/PAST server */ dpnhCaps.dwSize = sizeof(dpnhCaps); hr = IDirectPlayNATHelp_GetCaps(pDirectPlayNATHelp, &dpnhCaps, DPNHGETCAPS_UPDATESERVERSTATUS); if (hr != DPNH_OK && hr != DPNHSUCCESS_ADDRESSESCHANGED) { IMPORTANT_MSG((L"IDirectPlayNATHelp_GetCaps failed")); DumpLibHr(hr); dwRet = ERROR_BAD_UNIT; goto done; } if (dpnhCaps.dwFlags & (DPNHCAPSFLAG_LOCALFIREWALLPRESENT | DPNHCAPSFLAG_GATEWAYPRESENT)) { PIP_ADAPTER_INFO pAdpInfo = NULL; SOCKADDR_IN saddrOurLAN; PMIB_IPADDRTABLE pmib=NULL; ULONG ulSize = 0; DWORD dw; g_boolIcsPresent = TRUE; TRIVIAL_MSG((L"ICS server found using %S", pDll->szDllName)); g_lpszDllName = pDll->szDllName; // copy this pointer where it will do us some good g_pDPNH = pDirectPlayNATHelp; g_boolUsingNatHelp = TRUE; g_boolUsingNatPAST = pDll->bUsesUpnp; dwRet = ERROR_SUCCESS; // is this machine an ICS host? if (dpnhCaps.dwFlags & (DPNHCAPSFLAG_LOCALFIREWALLPRESENT | DPNHCAPSFLAG_GATEWAYISLOCAL)) g_boolIcsOnThisMachine = TRUE; ZeroMemory(&saddrOurLAN, sizeof(saddrOurLAN)); saddrOurLAN.sin_family = AF_INET; dw = GetAdaptersInfo( pAdpInfo, &ulSize ); if (dw == ERROR_BUFFER_OVERFLOW && ulSize) { pAdpInfo = (IP_ADAPTER_INFO*)malloc(ulSize); if (!pAdpInfo) goto done; dw = GetAdaptersInfo( pAdpInfo, &ulSize); if (dw == ERROR_SUCCESS) { PIP_ADAPTER_INFO p; PIP_ADDR_STRING ps; for(p=pAdpInfo; p!=NULL; p=p->Next) { for(ps = &(p->IpAddressList); ps; ps=ps->Next) { if (strcmp(ps->IpAddress.String, "0.0.0.0") != 0) { // blah blah blah saddrOurLAN.sin_addr.S_un.S_addr = inet_addr(ps->IpAddress.String); TRIVIAL_MSG((L"Initializing RSIP to LAN adapter at [%S]", ps->IpAddress.String)); goto doit; } } } } } doit: memcpy(&g_saddrLocal, &saddrOurLAN, sizeof(saddrOurLAN)); // Does the ICS have a public address? if ( 1 ) { /* then we will discover the public address */ DPNHHANDLE dpHnd; saddrOurLAN.sin_port = ntohs(3389); /* first we ask for a new mapping */ hr = IDirectPlayNATHelp_RegisterPorts(g_pDPNH, (SOCKADDR *)&saddrOurLAN, sizeof(saddrOurLAN), 1, 30000, &dpHnd, DPNHREGISTERPORTS_TCP); if (hr != DPNH_OK) { IMPORTANT_MSG((L"IDirectPlayNATHelp_RegisterPorts failed in StartDpNatHlp")); DumpLibHr(hr); } else { /* we succeeded, so query for the address */ SOCKADDR_IN lsi; DWORD dwSize, dwTypes; TRIVIAL_MSG((L"IDirectPlayNATHelp_RegisterPorts Assigned: 0x%x", dpHnd)); dwSize = sizeof(lsi); ZeroMemory(&lsi, dwSize); hr = IDirectPlayNATHelp_GetRegisteredAddresses(g_pDPNH, dpHnd, (SOCKADDR *)&lsi, &dwSize, &dwTypes, NULL, 0, ); if (hr == DPNH_OK && dwSize) { wsprintfA(g_szPublicAddr, "%d.%d.%d.%d", lsi.sin_addr.S_un.S_un_b.s_b1, lsi.sin_addr.S_un.S_un_b.s_b2, lsi.sin_addr.S_un.S_un_b.s_b3, lsi.sin_addr.S_un.S_un_b.s_b4); TRIVIAL_MSG((L"Public Address=[%S]", g_szPublicAddr )); } else { IMPORTANT_MSG((L"GetRegisteredAddresses[0x%x] failed, size=0x%x", dpHnd, dwSize)); DumpLibHr(hr); } /* close out the temp port we got */ hr = IDirectPlayNATHelp_DeregisterPorts(g_pDPNH, dpHnd, 0); if (hr != DPNH_OK) { IMPORTANT_MSG((L"DeregisterPorts failed in ClosePort")); DumpLibHr(hr); dwRet = ERROR_INVALID_ACCESS; } } } } else { hr = IDirectPlayNATHelp_Close(pDirectPlayNATHelp, 0); if (hr != DPNH_OK) { IMPORTANT_MSG((L"IDirectPlayNATHelp_Close failed")); DumpLibHr(hr); } hr = IDirectPlayNATHelp_Release(pDirectPlayNATHelp); if (hr != DPNH_OK) { IMPORTANT_MSG((L"IDirectPlayNATHelp_Release failed")); DumpLibHr(hr); } pDll++; if (pDll->szDllName) { if (hMod) FreeLibrary(hMod); hMod = 0; goto try_again; } g_pDPNH = NULL; g_boolUsingNatHelp = FALSE; dwRet = ERROR_BAD_UNIT; } done: if (dwRet != ERROR_SUCCESS) { if (hMod) FreeLibrary(hMod); hMod = 0; } TRIVIAL_MSG((L"done with StartDpNatHlp, result=0x%x", dwRet)); // save hMod so we can close it out in StopICSLibrary g_hModDpNatHlp = hMod; return dwRet; }; /**************************************************************************** ** ** The first call to be made into this library. It is responsible for ** starting up all worker threads, initializing all memory and libs, ** and starting up the DPHLPAPI.DLL function (if found). ** ****************************************************************************/ DWORD APIENTRY StartICSLib(void) { WSADATA WsaData; DWORD dwThreadId; HANDLE hEvent, hThread; HKEY hKey; int sktRet; // open reg key first, to get ALL the spew... if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\ICSHelper", 0, KEY_READ, &hKey)) { DWORD dwSize; dwSize = sizeof(gDbgFlag); RegQueryValueEx(hKey, L"DebugSpew", NULL, NULL, (LPBYTE)&gDbgFlag, &dwSize); g_waitDuration = 0; dwSize = sizeof(g_waitDuration); RegQueryValueEx(hKey, L"RetryTimeout", NULL, NULL, (LPBYTE)&g_waitDuration, &dwSize); if (g_waitDuration) g_waitDuration *= 1000; else g_waitDuration = 120000; RegCloseKey(hKey); } // should we create a debug log file? if (gDbgFlag & DBG_MSG_DEST_FILE) { WCHAR szLogfileName[MAX_PATH]; GetSystemDirectory(szLogfileName, sizeof(szLogfileName)/sizeof(szLogfileName[0])); lstrcat(szLogfileName, L"\\SalemICSHelper.log"); iDbgFileHandle = _wopen(szLogfileName, _O_APPEND | _O_BINARY | _O_RDWR, 0); if (-1 != iDbgFileHandle) { OutputDebugStringA("opened debug log file\n"); } else { unsigned char UniCode[2] = {0xff, 0xfe}; // we must create the file OutputDebugStringA("must create debug log file"); iDbgFileHandle = _wopen(szLogfileName, _O_BINARY | _O_CREAT | _O_RDWR, _S_IREAD | _S_IWRITE); if (-1 != iDbgFileHandle) _write(iDbgFileHandle, &UniCode, sizeof(UniCode)); else { OutputDebugStringA("ERROR: failed to create debug log file"); iDbgFileHandle = 0; } } } g_iPort = GetTsPort(); TRIVIAL_MSG((L"StartICSLib()" )); ZeroMemory(g_PortHandles, sizeof(g_PortHandles)); if (g_boolInitialized) { HEINOUS_E_MSG((L"ERROR: StartICSLib called twice")); return ERROR_INVALID_PARAMETER; } else g_boolInitialized = TRUE; // of course, this must be done... InitializeCriticalSection(&g_CritSec); // create an event for later use by the daemon thread hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (!hEvent) { IMPORTANT_MSG((L"Could not create an event for RSIP worker thread, err=0x%x", GetLastError())); return GetLastError(); } g_hThreadEvent = hEvent; if (0 != WSAStartup(MAKEWORD(2,2), &WsaData)) { if (0 != (sktRet = WSAStartup(MAKEWORD(2,0), &WsaData))) { IMPORTANT_MSG((L"WSAStartup failed:")); return sktRet; } } if (ERROR_SUCCESS == StartDpNatHlp()) { // start RSIP daemon process, which will do all the work hThread = CreateThread( NULL, // SD- not needed 0, // Stack Size (LPTHREAD_START_ROUTINE)DpNatHlpThread, NULL, 0, &dwThreadId ); } else { // start up RSIP lib g_szPublicAddr[0]=0; if (RsipIsRunningOnThisMachine( &g_saddrPublic )) { // makes casting easier SOCKADDR_IN *pPubAddr = (SOCKADDR_IN*)( &g_saddrPublic ); g_boolIcsPresent = g_boolIcsOnThisMachine = TRUE; // save the public side address so that we can filter it out // of the address list we return wsprintfA(g_szPublicAddr, "%d.%d.%d.%d", pPubAddr->sin_addr.S_un.S_un_b.s_b1, pPubAddr->sin_addr.S_un.S_un_b.s_b2, pPubAddr->sin_addr.S_un.S_un_b.s_b3, pPubAddr->sin_addr.S_un.S_un_b.s_b4); INTERESTING_MSG((L"PAST Host is local- public address is [%S]", &g_szPublicAddr)); } else { SOCKADDR_IN saddrOurLAN; PMIB_IPADDRTABLE pmib=NULL; ULONG ulSize = 0; DWORD dw; PIP_ADAPTER_INFO pAdpInfo = NULL; ZeroMemory(&saddrOurLAN, sizeof(saddrOurLAN)); saddrOurLAN.sin_family = AF_INET; dw = GetAdaptersInfo( pAdpInfo, &ulSize ); if (dw == ERROR_BUFFER_OVERFLOW && ulSize) { pAdpInfo = (IP_ADAPTER_INFO*)malloc(ulSize); if (!pAdpInfo) goto done; dw = GetAdaptersInfo( pAdpInfo, &ulSize); if (dw == ERROR_SUCCESS) { PIP_ADAPTER_INFO p; PIP_ADDR_STRING ps; for(p=pAdpInfo; p!=NULL; p=p->Next) { for(ps = &(p->IpAddressList); ps; ps=ps->Next) { if (strcmp(ps->IpAddress.String, "0.0.0.0") != 0) { // blah blah blah saddrOurLAN.sin_addr.S_un.S_addr = inet_addr(ps->IpAddress.String); TRIVIAL_MSG((L"Initializing RSIP to LAN adapter at [%S]", ps->IpAddress.String)); goto doit; } } } } } doit: g_boolIcsPresent = Initialize((SOCKADDR *)&saddrOurLAN, FALSE); if (g_boolIcsPresent) { DWORD dwBindID=0; SOCKADDR saddrAssigned; if (S_OK == AssignPort( FALSE, 6969, &saddrAssigned, &dwBindID )) { // free the port, since we just wanted the public IP address FreePort( dwBindID ); wsprintfA(g_szPublicAddr, "%d.%d.%d.%d", (UCHAR)saddrAssigned.sa_data[2], (UCHAR)saddrAssigned.sa_data[3], (UCHAR)saddrAssigned.sa_data[4], (UCHAR)saddrAssigned.sa_data[5]); INTERESTING_MSG((L"PAST Host is NOT local- public address is [%S]", &g_szPublicAddr)); } else { IMPORTANT_MSG((L"Failed to assign port when attempting to determine public network address!" )); } } } done: g_lpszDllName = szInternal; // start RSIP daemon process, which will do all the work hThread = CreateThread( NULL, // SD- not needed 0, // Stack Size (LPTHREAD_START_ROUTINE)RsipDaemonThread, NULL, 0, &dwThreadId ); } if (!hThread) { IMPORTANT_MSG((L"Could not create RSIP worker thread, err=0x%x", GetLastError())); return GetLastError(); } // save this for later, as we need it in the close function g_hWorkerThread = hThread; TRIVIAL_MSG((L"StartICSLib() returning ERROR_SUCCESS")); return(ERROR_SUCCESS); } /**************************************************************************** ** ** The last call to be made into this library. Do not call any other ** functiopn in this library after you call this! ** ****************************************************************************/ DWORD APIENTRY StopICSLib(void) { DWORD dwRet = ERROR_SUCCESS; DWORD dwTmp; TRIVIAL_MSG((L"StopICSLib()" )); // signal the worker thread, so that it will do a shutdown SetEvent(g_hThreadEvent); // then wait for it to shutdown. dwTmp = WaitForSingleObject(g_hWorkerThread, 15000); if (dwTmp == WAIT_OBJECT_0) TRIVIAL_MSG((L"ICS worker thread closed down normally")); else if (dwTmp == WAIT_ABANDONED) IMPORTANT_MSG((L"ICS worker thread did not complete in 15 seconds")); else IMPORTANT_MSG((L"WaitForWorkerThread failed")); CloseHandle(g_hWorkerThread); TRIVIAL_MSG((L"StopICSLib() returning 0x%x", dwRet )); _close(iDbgFileHandle); g_boolInitialized = FALSE; return(dwRet); } /**************************************************************************** ** ** SetAlertEvent ** Pass in an event handle. Then, whenever the ICS changes state, I ** will signal that event. ** ****************************************************************************/ DWORD APIENTRY SetAlertEvent(HANDLE hEvent) { TRIVIAL_MSG((L"SetAlertEvent(0x%x)", hEvent)); g_hAlertEvent = hEvent; return 1; } /**************************************************************************** ** ** Address String Compression routines ** ***************************************************************************** ** ** The following is a group of routines designed to compress and expand ** IPV4 addresses into the absolute minimum size possible. This is to ** provide a compressed ASCII string that can be parsed using standard ** shell routines for command line parsing. ** The compressed string has the following restrictions: ** -> Must not expand to more characters if UTF8 encoded. ** -> Must not contain the NULL character so that string libs work. ** -> Cannot contain double quote character, the shell needs that. ** -> Does not have to be human readable. ** ** Data Types: ** There are three data types used here: ** szAddr The orginal IPV4 string address ("X.X.X.X:port") ** blobAddr Six byte struct with 4 bytes of address, and 2 bytes of port ** szComp Eight byte ascii string of compressed IPV4 address ** ****************************************************************************/ #define COMP_OFFSET '#' #define COMP_SEPERATOR '!' #pragma pack(push,1) typedef struct _BLOB_ADDR { UCHAR addr_d; // highest order address byte UCHAR addr_c; UCHAR addr_b; UCHAR addr_a; // lowest order byte (last in IP string address) WORD port; } BLOB_ADDR, *PBLOB_ADDR; #pragma pack(pop) WCHAR b64Char[64]={ 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z', 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z', '0','1','2','3','4','5','6','7','8','9','+','/' }; void DumpBlob(PBLOB_ADDR pba) { UCHAR *lpa; lpa = (UCHAR *)pba; TRIVIAL_MSG((L"blob (hex): %x,%x,%x,%x,%x,%x", *lpa, *(lpa+1), *(lpa+2), *(lpa+3), *(lpa+4), *(lpa+5))); TRIVIAL_MSG((L" (struct): %x,%x,%x,%x:%x", pba->addr_d, pba->addr_c, pba->addr_b, pba->addr_a, pba->port)); TRIVIAL_MSG((L" (IP): %d.%d.%d.%d:%d", pba->addr_d, pba->addr_c, pba->addr_b, pba->addr_a, pba->port)); } /**************************************************************************** ** char * atob(char *szVal, UCHAR *result) ****************************************************************************/ WCHAR *atob(WCHAR *szVal, UCHAR *result) { WCHAR *lptr; WCHAR ucb; UCHAR foo; if (!result || !szVal) { IMPORTANT_MSG((L"ERROR: NULL ptr passed in atob")); return NULL; } // start ptr at the beginning of string lptr = szVal; foo = 0; ucb = *lptr++ - '0'; while (ucb >= 0 && ucb <= 9) { foo *= 10; foo += ucb; ucb = (*lptr++)-'0'; } *result = (UCHAR)foo; return lptr; } /**************************************************************************** ** ** CompressAddr(pszAddr, pblobAddr); ** Takes an ascii IP address (X.X.X.X:port) and converts it to a ** 6 byte binary blob. ** ** returns TRUE for success, FALSE for failure. ** ****************************************************************************/ BOOL CompressAddr(WCHAR *pszAddr, PBLOB_ADDR pblobAddr) { BLOB_ADDR lblob; WCHAR *lpsz; if (!pszAddr || !pblobAddr) { IMPORTANT_MSG((L"ERROR: NULL ptr passed in CompressAddr")); return FALSE; } lpsz = pszAddr; lpsz = atob(lpsz, &lblob.addr_d); if (*(lpsz-1) != '.') { IMPORTANT_MSG((L"ERROR: bad address[0] passed in CompressAddr for %s", pszAddr)); return FALSE; } lpsz = atob(lpsz, &lblob.addr_c); if (*(lpsz-1) != '.') { IMPORTANT_MSG((L"ERROR: bad address[1] passed in CompressAddr")); return FALSE; } lpsz = atob(lpsz, &lblob.addr_b); if (*(lpsz-1) != '.') { IMPORTANT_MSG((L"ERROR: bad address[2] passed in CompressAddr")); return FALSE; } lpsz = atob(lpsz, &lblob.addr_a); // is there a port number here? if (*(lpsz-1) == ':') lblob.port = (WORD)_wtoi(lpsz); else lblob.port = 0; // copy back the result memcpy(pblobAddr, &lblob, sizeof(*pblobAddr)); return TRUE; } /**************************************************************************** ** ** ExpandAddr(pszAddr, pblobAddr); ** Takes 6 byte binary blob and converts it into an ascii IP ** address (X.X.X.X:port) ** ** returns TRUE for success, FALSE for failure. ** ****************************************************************************/ BOOL ExpandAddr(WCHAR *pszAddr, PBLOB_ADDR pba) { if (!pszAddr || !pba) { IMPORTANT_MSG((L"ERROR: NULL ptr passed in ExpandAddr")); return FALSE; } wsprintf(pszAddr, L"%d.%d.%d.%d", pba->addr_d, pba->addr_c, pba->addr_b, pba->addr_a); if (pba->port) { WCHAR scratch[8]; wsprintf(scratch, L":%d", pba->port); wcscat(pszAddr, scratch); } return TRUE; } /**************************************************************************** ** ** AsciifyAddr(pszAddr, pblobAddr); ** Takes 6 byte binary blob and converts it into compressed ascii ** will return either 6 or 8 bytes of string ** ** returns TRUE for success, FALSE for failure. ** ****************************************************************************/ BOOL AsciifyAddr(WCHAR *pszAddr, PBLOB_ADDR pba) { UCHAR tmp; DWORDLONG dwl; int i, iCnt; if (!pszAddr || !pba) { IMPORTANT_MSG((L"ERROR: NULL ptr passed in AsciifyAddr")); return FALSE; } iCnt = 6; if (pba->port) iCnt = 8; dwl = 0; memcpy(&dwl, pba, sizeof(*pba)); for (i = 0; i < iCnt; i++) { // get 6 bits of data tmp = (UCHAR)(dwl & 0x3f); // add the offset to asciify this // offset must be bigger the double-quote char. pszAddr[i] = b64Char[tmp]; // (WCHAR)(tmp + COMP_OFFSET); // Shift right 6 bits dwl = Int64ShrlMod32(dwl, 6); } // terminating NULL pszAddr[iCnt] = 0; return TRUE; } /**************************************************************************** ** ** DeAsciifyAddr(pszAddr, pblobAddr); ** Takes a compressed ascii string and converts it into a ** 6 or 8 byte binary blob ** ** returns TRUE for success, FALSE for failure. ** ****************************************************************************/ BOOL DeAsciifyAddr(WCHAR *pszAddr, PBLOB_ADDR pba) { UCHAR tmp; WCHAR wtmp; DWORDLONG dwl; int i; int iCnt; if (!pszAddr || !pba) { IMPORTANT_MSG((L"ERROR: NULL ptr passed in DeAsciifyAddr")); return FALSE; } /* how long is this string? * if it is 6 bytes, then there is no port * else it should be 8 bytes */ i = wcslen(pszAddr); if (i == 6 || i == 8) iCnt = i; else { iCnt = 8; IMPORTANT_MSG((L"Strlen (%d) is wrong in DeAsciifyAddr", i)); } dwl = 0; for (i = iCnt-1; i >= 0; i--) { wtmp = pszAddr[i]; if (wtmp >= L'A' && wtmp <= L'Z') tmp = wtmp - L'A'; else if (wtmp >= L'a' && wtmp <= L'z') tmp = wtmp - L'a' + 26; else if (wtmp >= L'0' && wtmp <= L'9') tmp = wtmp - L'0' + 52; else if (wtmp == L'+') tmp = 62; else if (wtmp == L'/') tmp = 63; else { tmp = 0; HEINOUS_E_MSG((L"ERROR:found invalid character in decode stream")); } // tmp = (UCHAR)(pszAddr[i] - COMP_OFFSET); if (tmp > 63) { tmp = 0; HEINOUS_E_MSG((L"ERROR:screwup in DeAsciify")); } dwl = Int64ShllMod32(dwl, 6); dwl |= tmp; } memcpy(pba, &dwl, sizeof(*pba)); return TRUE; } /**************************************************************************** ** ** SquishAddress(char *szIp, char *szCompIp) ** Takes one IP address and compresses it to minimum size ** ****************************************************************************/ DWORD APIENTRY SquishAddress(WCHAR *szIp, WCHAR *szCompIp) { WCHAR *thisAddr, *nextAddr; BLOB_ADDR ba; if (!szIp || !szCompIp) { HEINOUS_E_MSG((L"SquishAddress called with NULL ptr")); return ERROR_INVALID_PARAMETER; } // TRIVIAL_MSG((L"SquishAddress(%s)", szIp)); thisAddr = szIp; szCompIp[0] = 0; while (thisAddr) { WCHAR scr[10]; nextAddr = wcschr(thisAddr, L';'); if (nextAddr && *(nextAddr+1)) { *nextAddr = 0; } else nextAddr=0; CompressAddr(thisAddr, &ba); // DumpBlob(&ba); AsciifyAddr(scr, &ba); wcscat(szCompIp, scr); if (nextAddr) { // restore seperator found earlier *nextAddr = ';'; nextAddr++; wcscat(szCompIp, L"!" /* COMP_SEPERATOR */); } thisAddr = nextAddr; } // TRIVIAL_MSG((L"SquishAddress returns [%s]", szCompIp)); return ERROR_SUCCESS; } /**************************************************************************** ** ** ExpandAddress(char *szIp, char *szCompIp) ** Takes a compressed IP address and returns it to ** "normal" ** ****************************************************************************/ DWORD APIENTRY ExpandAddress(WCHAR *szIp, WCHAR *szCompIp) { BLOB_ADDR ba; WCHAR *thisAddr, *nextAddr; if (!szIp || !szCompIp) { HEINOUS_E_MSG((L"ExpandAddress called with NULL ptr")); return ERROR_INVALID_PARAMETER; } // TRIVIAL_MSG((L"ExpandAddress(%s)", szCompIp)); thisAddr = szCompIp; szIp[0] = 0; while (thisAddr) { WCHAR scr[32]; nextAddr = wcschr(thisAddr, COMP_SEPERATOR); if (nextAddr) *nextAddr = 0; DeAsciifyAddr(thisAddr, &ba); // DumpBlob(&ba); ExpandAddr(scr, &ba); wcscat(szIp, scr); if (nextAddr) { // restore seperator found earlier *nextAddr = COMP_SEPERATOR; nextAddr++; wcscat(szIp, L";"); } thisAddr = nextAddr; } // TRIVIAL_MSG((L"ExpandAddress returns [%s]", szIp)); return ERROR_SUCCESS; } /**************************************************************************** ** ** FetchAllAddressesEx ** Returns a string listing all the valid IP addresses for the machine ** controlled by a set of "flags". These are as follows: ** IPflags= ** IPF_ADD_DNS adds the DNS name to the IP list ** IPF_COMPRESS compresses the IP address list (exclusive w/ IPF_ADD_DNS) ** IPF_NO_SORT do not sort adapter IP list ** ** Formatting details: ** 1. Each address is seperated with a ";" (semicolon) ** 2. Each address consists of the "1.2.3.4", and is followed by ":p" ** where the colon is followed by the port number ** ****************************************************************************/ #define WCHAR_CNT 4096 DWORD APIENTRY FetchAllAddressesEx(WCHAR *lpszAddr, int iBufSize, int IPflags) { DWORD dwRet = 1; WCHAR *AddressLst; int iAddrLen; BOOL bSort=FALSE; AddressLst = (WCHAR *) malloc(WCHAR_CNT * sizeof(WCHAR)); if (!AddressLst) { HEINOUS_E_MSG((L"Fatal error: malloc failed in FetchAllAddressesEx")); return 0; } *AddressLst = 0; INTERESTING_MSG((L"FetchAllAddressesEx()" )); // sanity check the flags if ((IPflags & IPF_COMPRESS) && IPflags != IPF_COMPRESS+IPF_NO_SORT) { IMPORTANT_MSG((L"Illegal IPflags value (0x%x) in FetchAllAddressesEx, forcing to IPF_COMPRESS+IPF_NO_SORT", IPflags)); IPflags = IPF_COMPRESS+IPF_NO_SORT; } if (g_boolIcsPresent && g_pDPNH) { if (g_boolUsingNatHelp) { int i; // gotta cycle through the g_PortHandles list... for (i=0;iiMapped; j++) { dwSize = sizeof(lsi); ZeroMemory(&lsi, dwSize); hr = IDirectPlayNATHelp_GetRegisteredAddresses(g_pDPNH, pMap->hMapped[j], (SOCKADDR *)&lsi, &dwSize, &dwTypes, NULL, 0, ); if (hr == DPNH_OK && dwSize) { WCHAR scratch[32]; wsprintf(scratch, L"%d.%d.%d.%d:%d;", lsi.sin_addr.S_un.S_un_b.s_b1, lsi.sin_addr.S_un.S_un_b.s_b2, lsi.sin_addr.S_un.S_un_b.s_b3, lsi.sin_addr.S_un.S_un_b.s_b4, ntohs( lsi.sin_port )); wcscat(AddressLst, scratch); TRIVIAL_MSG((L"GetRegisteredAddresses(0x%x)=[%s]", g_PortHandles[i], scratch )); } else { IMPORTANT_MSG((L"GetRegisteredAddresses[0x%x] failed, size=0x%x", g_PortHandles[i], dwSize)); DumpLibHr(hr); } } goto got_addresses; } } } else { union AddrV4 { DWORD d; BYTE b[4]; }; WCHAR scratch[32]; union AddrV4 a; RSIP_LEASE_RECORD *pLeaseWalker; RSIP_LEASE_RECORD *pNextLease; pLeaseWalker = g_pRsipLeaseRecords; while( pLeaseWalker ) { bSort=TRUE; pNextLease = pLeaseWalker->pNext; a.d = pLeaseWalker->addrV4; wsprintf(scratch, L"%d.%d.%d.%d:%d;", a.b[0], a.b[1], a.b[2], a.b[3], ntohs(pLeaseWalker->rport)); wcscat(AddressLst, scratch); pLeaseWalker=pNextLease; } } } got_addresses: iAddrLen = wcslen(AddressLst); GetIPAddress(AddressLst+iAddrLen, WCHAR_CNT-iAddrLen, g_iPort); if (IPflags & IPF_ADD_DNS) { WCHAR *DnsName=NULL; DWORD dwNameSz=0; GetComputerNameEx(ComputerNamePhysicalDnsFullyQualified, NULL, &dwNameSz); dwNameSz++; DnsName = (WCHAR *)malloc(dwNameSz * sizeof(WCHAR)); if (DnsName) { *DnsName = 0; if (GetComputerNameEx(ComputerNamePhysicalDnsFullyQualified, DnsName, &dwNameSz)) { if ((dwNameSz + iAddrLen) < WCHAR_CNT-4) wcscat(AddressLst, DnsName); if (g_iPort) { WCHAR scr[16]; wsprintf(scr, L":%d", g_iPort); wcscat(AddressLst, scr); } } free(DnsName); } } if (!(IPflags & IPF_NO_SORT) && bSort) { WCHAR *lpStart; WCHAR szLast[36]; int i=0; TRIVIAL_MSG((L"Sorting address list : %s", AddressLst)); lpStart = AddressLst+iAddrLen; while (*(lpStart+i) && *(lpStart+i) != L';') { szLast[i] = *(lpStart+i); i++; } szLast[i++]=0; wcscpy(lpStart, lpStart+i); TRIVIAL_MSG((L"inter sort: %s, %s", AddressLst, szLast)); wcscat(AddressLst, L";"); wcscat(AddressLst, szLast); TRIVIAL_MSG((L"sort done")); } if (IPflags & IPF_COMPRESS) { WCHAR compBfr[1000]; compBfr[0]=0; SquishAddress(AddressLst, compBfr); TRIVIAL_MSG((L"inner comp: %s, %s", AddressLst, compBfr)); wcscpy(AddressLst, compBfr); } dwRet = 1 + wcslen(AddressLst); if (lpszAddr && iBufSize >= (int)dwRet) memcpy(lpszAddr, AddressLst, dwRet*(sizeof(AddressLst[0]))); INTERESTING_MSG((L"Fetched all Ex-addresses:cnt=%d, sz=[%s]", dwRet, AddressLst)); free(AddressLst); return dwRet; } // it is hard to imagine a machine with this many simultaneous connections, but it is possible, I suppose #define RAS_CONNS 6 DWORD GetConnections() { DWORD dwRet; RASCONN *lpRasConn, *lpFree; DWORD lpcb, lpcConnections; int i; TRIVIAL_MSG((L"entered GetConnections")); lpFree = NULL; lpcb = RAS_CONNS * sizeof(RASCONN); lpRasConn = (LPRASCONN) malloc(lpcb); if (!lpRasConn) return 0; lpFree = lpRasConn; lpRasConn->dwSize = sizeof(RASCONN); lpcConnections = RAS_CONNS; dwRet = RasEnumConnections(lpRasConn, &lpcb, &lpcConnections); if (dwRet != 0) { IMPORTANT_MSG((L"RasEnumConnections failed: Error = %d", dwRet)); free(lpFree); return 0; } dwRet = 0; TRIVIAL_MSG((L"Found %d connections", lpcConnections)); if (lpcConnections) { for (i = 0; i < (int)lpcConnections; i++) { TRIVIAL_MSG((L"Entry name: %s, type=%s\n", lpRasConn->szEntryName, lpRasConn->szDeviceType)); if (!_wcsicmp(lpRasConn->szDeviceType, RASDT_Modem )) { TRIVIAL_MSG((L"Found a modem (%s)", lpRasConn->szDeviceName)); dwRet |= 1; } else if (!_wcsicmp(lpRasConn->szDeviceType, RASDT_Vpn)) { TRIVIAL_MSG((L"Found a VPN (%s)", lpRasConn->szDeviceName)); dwRet |= 2; } else { // probably ISDN, or something like that... TRIVIAL_MSG((L"Found something else, (%s)", lpRasConn->szDeviceType)); dwRet |= 4; } lpRasConn++; } } if (lpFree) free(lpFree); TRIVIAL_MSG((L"GetConnections returning 0x%x", dwRet)); return dwRet; } #undef RAS_CONNS /**************************************************************************** ** ** GetIcsStatus(PICSSTAT pStat) ** Returns a structure detailing much of what is going on inside this ** library. The dwSize entry must be filled in before calling this ** function. Use "sizeof(ICSSTAT))" to populate this. ** ****************************************************************************/ DWORD APIENTRY GetIcsStatus(PICSSTAT pStat) { DWORD dwSz; if (!pStat || pStat->dwSize > sizeof(ICSSTAT)) { HEINOUS_E_MSG((L"ERROR:Bad pointer or size passed in to GetIcsStatus")); return ERROR_INVALID_PARAMETER; } // clear out the struct dwSz = pStat->dwSize; ZeroMemory(pStat, dwSz); pStat->dwSize = dwSz; pStat->bIcsFound = g_boolIcsPresent; pStat->bIcsServer = g_boolIcsOnThisMachine; pStat->bUsingDP = g_boolUsingNatHelp; if (g_boolUsingNatHelp) pStat->bUsingUpnp = !g_boolUsingNatPAST; if (g_boolIcsPresent) { wsprintf(pStat->wszPubAddr, L"%S", g_szPublicAddr); wsprintf(pStat->wszLocAddr, L"%d.%d.%d.%d", g_saddrLocal.sin_addr.S_un.S_un_b.s_b1, g_saddrLocal.sin_addr.S_un.S_un_b.s_b2, g_saddrLocal.sin_addr.S_un.S_un_b.s_b3, g_saddrLocal.sin_addr.S_un.S_un_b.s_b4); wsprintf(pStat->wszDllName, L"%S", g_lpszDllName); } else wsprintf(pStat->wszDllName, L"none"); dwSz = GetConnections(); if (dwSz & 1) pStat->bModemPresent = TRUE; if (dwSz & 2) pStat->bVpnPresent = TRUE; return ERROR_SUCCESS; } /************************************************************************************* ** ** *************************************************************************************/ int GetTsPort(void) { DWORD dwRet = 3389; HKEY hKey; // open reg key first, to get ALL the spew...HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Terminal Server\\Wds\\rdpwd\\Tds\\tcp if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Terminal Server\\Wds\\rdpwd\\Tds\\tcp", 0, KEY_READ, &hKey)) { DWORD dwSize; dwSize = sizeof(dwRet); RegQueryValueEx(hKey, L"PortNumber", NULL, NULL, (LPBYTE)&dwRet, &dwSize); RegCloseKey(hKey); } return dwRet; }