//+---------------------------------------------------------------------------- // // File: cmroute.cpp // // Module: CMROUTE.DLL // // Synopsis: Route Plumbing implementation for CM, as a post-connect action // // Copyright (c) 1998-2000 Microsoft Corporation // // Author: 12-Mar-2000 SumitC Created // // Note: // //----------------------------------------------------------------------------- #include "pch.h" #include "iphlpapi.h" #include "cmdebug.h" // // Include the shared source code for SuppressInetAutoDial and SetInetStateConnected // #include "inetopt.cpp" // // Include the locale-safe replacement for lstrcmpi // #include "CompareString.cpp" // // Function declarations // HRESULT ParseArgs(LPSTR pszArgList, BOOL * pfUseFile, LPSTR pszRoutesFile, BOOL * pfUseURL, LPSTR pszRoutesURL, BOOL * pfProfile, LPSTR pszProfile, BOOL * pfURLNotFoundIgnorable, BOOL * pfStayAliveOnAccessDenied, BOOL * pfKeepTempFiles); #if 0 // see note below HRESULT CheckIPForwarding(); #endif HRESULT Initialize(PMIB_IPFORWARDTABLE * pRouteTable, PMIB_IPFORWARDROW * pGateway); HRESULT GetRoutesFromFile(LPSTR pszFileName, LPSTR pszProfile, LPSTR * ppszRouteInfo, DWORD * pcbRouteInfo); HRESULT GetRoutesFromURL(LPSTR pszURL, BOOL fKeepTempFiles, LPSTR * ppszRouteInfo, DWORD * pcbRouteInfo); HRESULT ProcessRouteInfo(const LPSTR pszNewRouteInfo, DWORD cbNewRouteInfo, PMIB_IPFORWARDTABLE pmibRouteTable, PMIB_IPFORWARDROW pGateway, BOOL * pfDeleteDefaultGateway); HRESULT DeleteDefaultGateway(PMIB_IPFORWARDROW pGateway); // // Route Table functions // HRESULT GetRouteTable(PMIB_IPFORWARDTABLE * pTable); DWORD GetIf(const MIB_IPFORWARDROW& route, const MIB_IPFORWARDTABLE& RouteTable); PMIB_IPFORWARDROW GetDefaultGateway(PMIB_IPFORWARDTABLE pRouteTable); // // Helper functions // BOOL ConvertSzToIP(LPSTR sz, DWORD& dwIP); LPTSTR IPtoTsz(DWORD dw); LPSTR IPtosz(DWORD dwIP, char *psz); LPSTR StrCpyWithoutQuotes(LPSTR pszDest, LPCSTR pszSrc); BOOL VerifyProfileAndGetServiceDir(LPSTR pszProfile); // // IP Helper function prototypes // typedef DWORD (WINAPI *pfnCreateIpForwardEntrySpec)(PMIB_IPFORWARDROW); typedef DWORD (WINAPI *pfnDeleteIpForwardEntrySpec)(PMIB_IPFORWARDROW); typedef DWORD (WINAPI *pfnGetIpForwardTableSpec)(PMIB_IPFORWARDTABLE, PULONG, BOOL); pfnCreateIpForwardEntrySpec g_pfnCreateIpForwardEntry = NULL; pfnDeleteIpForwardEntrySpec g_pfnDeleteIpForwardEntry = NULL; pfnGetIpForwardTableSpec g_pfnGetIpForwardTable = NULL; HMODULE g_hIpHlpApi = NULL; #if DBG void PrintRouteTable(); #endif //+---------------------------------------------------------------------------- // // Func: FreeIpHlpApis // // Desc: This function frees the instance of iphlpapi.dll loaded through // LoadIpHelpApis. Note that it also sets the module handle and all // of the function pointers loaded from this module to NULL. // // Args: None // // Return: Nothing // // Notes: // // History: 14-Dec-2000 quintinb Created // //----------------------------------------------------------------------------- void FreeIpHlpApis(void) { if (g_hIpHlpApi) { FreeLibrary(g_hIpHlpApi); g_hIpHlpApi = NULL; g_pfnCreateIpForwardEntry = NULL; g_pfnDeleteIpForwardEntry = NULL; g_pfnGetIpForwardTable = NULL; } } //+---------------------------------------------------------------------------- // // Func: LoadIpHelpApis // // Desc: This functions loads a copy of the iphlpapi.dll and then retrieves // function pointers for CreateIpForwardEntry, DeleteIpForwardEntry, // and GetIpForwardTable. The module handle and the function pointers // are stored in globals vars. // // Args: None // // Return: HRESULT - S_OK on success, S_FALSE on failure. This prevents cmroute // from returning an error value (which would stop the connection) // but allows cmroute to exit cleanly. // // Notes: // // History: 14-Dec-2000 quintinb Created // //----------------------------------------------------------------------------- HRESULT LoadIpHelpApis(void) { HRESULT hr = S_FALSE; // we want the connection to continue but cmroute to not do anything... g_hIpHlpApi = LoadLibrary(TEXT("IPHLPAPI.DLL")); if (g_hIpHlpApi) { g_pfnCreateIpForwardEntry = (pfnCreateIpForwardEntrySpec)GetProcAddress(g_hIpHlpApi, "CreateIpForwardEntry"); g_pfnDeleteIpForwardEntry = (pfnDeleteIpForwardEntrySpec)GetProcAddress(g_hIpHlpApi, "DeleteIpForwardEntry"); g_pfnGetIpForwardTable = (pfnGetIpForwardTableSpec)GetProcAddress(g_hIpHlpApi, "GetIpForwardTable"); if (g_pfnCreateIpForwardEntry && g_pfnDeleteIpForwardEntry && g_pfnGetIpForwardTable) { hr = S_OK; } else { FreeIpHlpApis(); } } CMTRACEHR("LoadIpHelpApis", hr); return hr; } //+---------------------------------------------------------------------------- // // Func: SetRoutes // // Desc: The entry point for handling route munging for VPN connections. // This is a Connection Manager connect action and uses the CM connect // action format (see CMAK docs for more info). Thus the parameters // to the dll are passed via a string which contains parameters (see the // cmproxy spec for a list of the parameter values). // // Args: [hWnd] - window handle of caller // [hInst] - instance handle of caller // [pszArgs] - argument string for connect action // [nShow] - unused // // Return: HRESULT // // Notes: // // History: 12-Mar-2000 SumitC Created // //----------------------------------------------------------------------------- HRESULT WINAPI SetRoutes(HWND hWnd, HINSTANCE hInst, LPSTR pszArgs, int nShow) { HRESULT hr = S_OK; PMIB_IPFORWARDTABLE pRouteTable = NULL; PMIB_IPFORWARDROW pGateway = NULL; LPSTR pszRoutesFromFile = NULL; DWORD cbRoutesFromFile = 0; LPSTR pszRoutesFromURL = NULL; DWORD cbRoutesFromURL = 0; // results of parsing the commandline BOOL fUseFile = FALSE; BOOL fUseURL = FALSE; BOOL fProfile = FALSE; BOOL fURLNotFoundIsNotFatal = FALSE; BOOL fStayAliveOnAccessDenied = FALSE; BOOL fDeleteDefaultGatewayViaFile = FALSE; BOOL fDeleteDefaultGatewayViaURL = FALSE; BOOL fKeepTempFiles = FALSE; char szRoutesFile[MAX_PATH + 1]; char szRoutesURL[MAX_PATH + 1]; char szProfile[MAX_PATH + 1]; #if 0 /* // start security check to block unauthorized users // REVIEW: remove before shipping! // // Quick and dirty security test. See if we can open hard-coded file first. // If file is not available, then bail out completely. // lstrcpy(szRoutesFile, "\\\\sherpa\\route-plumb\\msroutes.txt"); HANDLE hFile = CreateFile(szRoutesFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hFile) { CMTRACE1("Unable to access file %s\n", szRoutesFile); MessageBox(NULL, "You are not authorized to use this tool.", "CMROUTE.DLL Custom Action", MB_OK); CloseHandle(hFile); return E_ACCESSDENIED; } CloseHandle(hFile); // end security check */ #endif // // See if we can get the function pointers we need in IP helper? // hr = LoadIpHelpApis(); if (S_OK != hr) { goto Cleanup; } // // parse args // hr = ParseArgs(pszArgs, &fUseFile, szRoutesFile, &fUseURL, szRoutesURL, &fProfile, szProfile, &fURLNotFoundIsNotFatal, &fStayAliveOnAccessDenied, &fKeepTempFiles); if (S_OK != hr) { goto Cleanup; } #if 0 // see note below hr = CheckIPForwarding(); if (S_FALSE == hr) { CMTRACE("SetRoutes: IP forwarding is enabled - cmroute won't do anything"); hr = S_OK; goto Cleanup; } if (S_OK != hr) { goto Cleanup; } #endif #if DBG PrintRouteTable(); #endif // // Get the routetable and default gateway // hr = Initialize(&pRouteTable, &pGateway); if (S_OK != hr) { goto Cleanup; } // // Get the routes out of the file if asked // if (fUseFile) { hr = GetRoutesFromFile(szRoutesFile, (fProfile ? szProfile : NULL), &pszRoutesFromFile, &cbRoutesFromFile); if (S_OK != hr) { goto Cleanup; } #if DBG OutputDebugString(pszRoutesFromFile); #endif } // // Get the routes out of the URL if asked // if (fUseURL) { hr = GetRoutesFromURL(szRoutesURL, fKeepTempFiles, &pszRoutesFromURL, &cbRoutesFromURL); if (S_OK != hr) { // // It might have been worth adding a clause below to restrict this // to "failures to access the URL", but this list of errorcodes is // likely to be large (and if the system is really hosed, we'll find // out soon enough). So, bypass *all* errors if /DONT_REQUIRE_URL // is set. // if (fURLNotFoundIsNotFatal) { // // If URL_Access_Failure_Not_Fatal is set, don't return an error. // However, we unset the flag so that we stop processing the URL. // CMTRACE("SetRoutes: dont_require_url is set, bypassing error"); fUseURL = FALSE; hr = S_OK; } else { goto Cleanup; } } #if DBG OutputDebugString(pszRoutesFromURL); #endif } // // Now set the routes // MYDBGASSERT(S_OK == hr); if (fUseFile) { hr = ProcessRouteInfo(pszRoutesFromFile, cbRoutesFromFile, pRouteTable, pGateway, &fDeleteDefaultGatewayViaFile); if (S_OK != hr) { CMTRACE1("SetRoutes: adding routes from FILE failed with %x", hr); goto Cleanup; } } MYDBGASSERT(S_OK == hr); if (fUseURL) { hr = ProcessRouteInfo(pszRoutesFromURL, cbRoutesFromURL, pRouteTable, pGateway, &fDeleteDefaultGatewayViaURL); if (S_OK != hr) { if ((E_UNEXPECTED == hr) && fURLNotFoundIsNotFatal) { // we use E_UNEXPECTED to indicate that the URL points to a .htm file // instead of the file containing just routes which is what we're // expecting. In this case, we ignore this error. // CMTRACE("html string found error ignored because Dont_Require_URL is set"); hr = S_OK; } else { CMTRACE1("SetRoutes: adding routes from URL failed with %x", hr); goto Cleanup; } } } // // Delete default gateway // MYDBGASSERT(S_OK == hr); if (fDeleteDefaultGatewayViaFile || fDeleteDefaultGatewayViaURL) { hr = DeleteDefaultGateway(pGateway); } Cleanup: // // cleanup and leave // if (pRouteTable) { VirtualFree(pRouteTable, 0, MEM_RELEASE); } FreeIpHlpApis(); // // If we failed because of an access denied error from iphlpapi, but the admin // has set the flag to keep the connection alive anyway, mask the error. // if ((HRESULT_FROM_WIN32(ERROR_NETWORK_ACCESS_DENIED) == hr) && fStayAliveOnAccessDenied) { CMTRACE("SetRoutes: masking ERROR_NETWORK_ACCESS_DENIED because of StayAlive flag"); hr = S_FALSE; } CMTRACEHR("SetRoutes", hr); return hr; } //+---------------------------------------------------------------------------- // // Func: GetNextToken // // Desc: utility function for parsing the argument string. Goes past leading // whitespace and extracts a string // // Args: [pszStart] - IN the argument string // [ppszEnd] - OUT where parsing for this arg ended // [pszOut] - INOUT array of size MAX_PATH to hold arg if found // // Return: BOOL, TRUE if another arg found, FALSE if not // // Notes: // // History: 12-Mar-2000 SumitC Created // //----------------------------------------------------------------------------- BOOL GetNextToken(LPSTR pszStart, LPSTR * ppszEnd, LPSTR pszOut) { MYDBGASSERT(pszStart); MYDBGASSERT(ppszEnd); LPSTR pszEnd = NULL; // clear leading white space while (isspace(*pszStart)) { pszStart++; } if (NULL == *pszStart) { // just white space, no arg return FALSE; } // // If this character is ", this is probably a quoted string, containing spaces. // In this case, the termination character is another ". Otherwise, assume // it is a regular string terminated by a space. // if ('"' == *pszStart) { // may be a string containing spaces. pszEnd = strchr(pszStart + 1, '"'); } if (NULL == pszEnd) { // // Either it's a regular string, or we couldn't find a terminating " char // so we fall back on space-delimited handling. // pszEnd = pszStart + 1; while (*pszEnd && !isspace(*pszEnd)) { pszEnd++; } pszEnd--; } UINT cLen = (UINT)(pszEnd - pszStart + 1); if (cLen + 1 > MAX_PATH) { return FALSE; } else { lstrcpyn(pszOut, pszStart, cLen + 1); *ppszEnd = ++pszEnd; return TRUE; } } //+---------------------------------------------------------------------------- // // Func: Initialize // // Desc: Initialization function, gets the route table and default gateway // // Args: [ppmibRouteTable] - return location for route table // [ppGateway] - return location for default gateway // // Return: HRESULT // // Notes: // // History: 12-Mar-2000 SumitC Created // //----------------------------------------------------------------------------- HRESULT Initialize( OUT PMIB_IPFORWARDTABLE * ppmibRouteTable, OUT PMIB_IPFORWARDROW * ppGateway) { HRESULT hr = S_OK; MYDBGASSERT(ppmibRouteTable); MYDBGASSERT(ppGateway); if (NULL == ppmibRouteTable || NULL == ppGateway) { return E_INVALIDARG; } hr = GetRouteTable(ppmibRouteTable); if (S_OK == hr) { MYDBGASSERT(*ppmibRouteTable); *ppGateway = GetDefaultGateway(*ppmibRouteTable); } CMTRACEHR("Initialize", hr); return hr; } //+---------------------------------------------------------------------------- // // Func: ParseArgs // // Desc: convert the argument list into flags for our use. // // Args: [pszArgList] - IN, the argument list // [the rest] - OUT, all the arg values returned // // Return: HRESULT // // Notes: // // History: 12-Mar-2000 SumitC Created // //----------------------------------------------------------------------------- HRESULT ParseArgs( LPSTR pszArgList, BOOL * pfUseFile, LPSTR pszRoutesFile, BOOL * pfUseURL, LPSTR pszRoutesURL, BOOL * pfProfile, LPSTR pszProfile, BOOL * pfURLNotFoundIgnorable, BOOL * pfStayAliveOnAccessDenied, BOOL * pfKeepTempFiles) { HRESULT hr = S_OK; char szArg[MAX_PATH]; // // verify arguments // if (NULL == pszArgList || 0 == lstrlen(pszArgList) || !pfUseFile || !pszRoutesFile || !pfUseURL || !pszRoutesURL || !pfProfile || !pszProfile || !pfURLNotFoundIgnorable || !pfKeepTempFiles) { return E_INVALIDARG; } CMTRACE1("ParseArgs: arg list is %s", pszArgList); // // set the defaults // *pfUseFile = *pfUseURL = *pfProfile = *pfURLNotFoundIgnorable = FALSE; // // process the Arglist // while (GetNextToken(pszArgList, &pszArgList, szArg)) { if (0 == SafeCompareString("/Static_File_Name", szArg)) { *pfUseFile = TRUE; if (!GetNextToken(pszArgList, &pszArgList, szArg)) { return E_INVALIDARG; } if (lstrlen(szArg) > MAX_PATH) { CMTRACE("ParseArgs: file name is bigger than MAX_PATH!!"); return E_INVALIDARG; } StrCpyWithoutQuotes(pszRoutesFile, szArg); } else if (0 == SafeCompareString("/Dont_Require_URL", szArg)) { *pfURLNotFoundIgnorable = TRUE; } else if (0 == SafeCompareString("/URL_Update_Path", szArg)) { *pfUseURL = TRUE; if (!GetNextToken(pszArgList, &pszArgList, szArg)) { return E_INVALIDARG; } if (lstrlen(szArg) > MAX_PATH) { CMTRACE("ParseArgs: URL name is bigger than MAX_PATH!!"); return E_INVALIDARG; } lstrcpy(pszRoutesURL, szArg); } else if (0 == SafeCompareString("/Profile", szArg)) { *pfProfile = TRUE; if (!GetNextToken(pszArgList, &pszArgList, szArg)) { return E_INVALIDARG; } if (lstrlen(szArg) > MAX_PATH) { CMTRACE("ParseArgs: Profile filename is bigger than MAX_PATH!!"); return E_INVALIDARG; } StrCpyWithoutQuotes(pszProfile, szArg); } else if (0 == SafeCompareString("/IPHlpApi_Access_Denied_OK", szArg)) { *pfStayAliveOnAccessDenied = TRUE; } else if (0 == SafeCompareString("/No_Delete", szArg)) { *pfKeepTempFiles = TRUE; } else { CMTRACE1("Cmroute: unrecognized parameter - %s", szArg); MYDBGASSERT("Cmroute - unrecognized parameter!!"); } } CMTRACEHR("ParseArgs", hr); return hr; } #if 0 // 2000/11/28 SumitC // It wasn't clear what the required action should be when IP forwarding was // detected (should the connection be dropped or not) and it is a little late // to add UI to Whistler. The 'Check IP forwarding' feature is thus removed. // // see Windows Db bug # 216558 for more details. //+---------------------------------------------------------------------------- // // Func: CheckIPForwarding // // Desc: checks to see if anything is enabled on the client machine that would // make us want to have cmroute not do anything // // Args: none // // Return: HRESULT // // Notes: // // History: 01-Nov-2000 SumitC Created // //----------------------------------------------------------------------------- HRESULT CheckIPForwarding() { HRESULT hr = S_OK; MIB_IPSTATS stats; if (NO_ERROR != GetIpStatistics(&stats)) { hr = HRESULT_FROM_WIN32(GetLastError()); } else { if (stats.dwForwarding) { hr = S_FALSE; } } CMTRACEHR("CheckIPForwarding", hr); return hr; } #endif //+---------------------------------------------------------------------------- // // Func: GetRoutesFromFile // // Desc: extracts the contents of the given file // // Args: [pszFileName] - IN, filename // [pszProfile] - IN, profile if available // [ppszRouteInfo] - OUT, the route table bytes // [pcbRouteInfo] - OUT, the route table size // // Return: HRESULT // // Notes: // // History: 12-Mar-2000 SumitC Created // //----------------------------------------------------------------------------- HRESULT GetRoutesFromFile( LPSTR pszFileName, LPSTR pszProfile, LPSTR * ppszRouteInfo, DWORD * pcbRouteInfo) { HRESULT hr = S_OK; HANDLE hFile = NULL; LPSTR psz = NULL; DWORD cb = 0; BOOL fRet; BY_HANDLE_FILE_INFORMATION info; MYDBGASSERT(pszFileName); MYDBGASSERT(ppszRouteInfo); MYDBGASSERT(pcbRouteInfo); if (NULL == pszFileName || NULL == ppszRouteInfo || NULL == pcbRouteInfo) { hr = E_INVALIDARG; goto Cleanup; } CMTRACE1("GetRoutesFromFile: filename is %s", pszFileName); // // open the file, and read its contents into a buffer // hFile = CreateFile(pszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hFile) { // // perhaps the full pathname for the routes file wasn't specified. If // a Profile was passed in, we extract the ServiceDir and try again, // using the ServiceDir as the path. // if (VerifyProfileAndGetServiceDir(pszProfile)) { char sz[2 * MAX_PATH + 1]; lstrcpy(sz, pszProfile); lstrcat(sz, pszFileName); CMTRACE1("GetRoutesFromFile: retrying with %s", sz); hFile = CreateFile(sz, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); } if (INVALID_HANDLE_VALUE == hFile) { hr = HRESULT_FROM_WIN32(GetLastError()); goto Cleanup; } } if (FALSE == GetFileInformationByHandle(hFile, &info)) { hr = E_FAIL; goto Cleanup; } if (0 == info.nFileSizeLow) { CMTRACE("Routes file is EMPTY!!"); hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); goto Cleanup; } psz = (LPSTR) VirtualAlloc(NULL, info.nFileSizeLow, MEM_COMMIT, PAGE_READWRITE); if (NULL == psz) { hr = E_OUTOFMEMORY; goto Cleanup; } fRet = ReadFile(hFile, psz, info.nFileSizeLow, &cb, NULL); if (FALSE == fRet) { hr = E_FAIL; goto Cleanup; } // success *ppszRouteInfo = psz; *pcbRouteInfo = cb; Cleanup: if (hFile) { CloseHandle(hFile); } if (S_OK != hr) { VirtualFree(psz, 0, MEM_RELEASE); } CMTRACEHR("GetRoutesFromFile", hr); return hr; } //+---------------------------------------------------------------------------- // // Func: GetRoutesFromURL // // Desc: extracts the contents of the given URL // // Args: [pszURL] - IN, the URL // [fKeepTempFiles] - IN, do not delete temp buffer file(s) // [ppszRouteInfo] - OUT, the route table bytes // [pcbRouteInfo] - OUT, the route table size // // Return: HRESULT // // Notes: // // History: 12-Mar-2000 SumitC Created // //----------------------------------------------------------------------------- HRESULT GetRoutesFromURL( LPSTR pszURL, BOOL fKeepTempFiles, LPSTR * ppszRouteInfo, DWORD * pcbRouteInfo) { HRESULT hr = S_OK; HINTERNET hInternet = NULL; HINTERNET hPage = NULL; LPBYTE pb = NULL; DWORD cb = 0; TCHAR szLocalBufferFile[MAX_PATH + 1]; DWORD cchLocalBuffer = 0; LPTSTR pszLocalBuffer = NULL; FILE * fp = NULL; BYTE Buffer[1024]; DWORD dwRead; MYDBGASSERT(pszURL); MYDBGASSERT(ppszRouteInfo); MYDBGASSERT(pcbRouteInfo); if (NULL == pszURL || NULL == ppszRouteInfo || NULL == pcbRouteInfo) { hr = E_INVALIDARG; goto Cleanup; } CMTRACE1("GetRoutesFromURL: URL is %s", pszURL); // // Get the path to the temp dir, if there is one. // cchLocalBuffer = GetTempPath(0, NULL); if (0 == cchLocalBuffer) { DWORD dwErr = GetLastError(); CMTRACE1(TEXT("GetTempPath failed, using current dir, GLE=%d"), dwErr); } else { cchLocalBuffer += (lstrlen(TEXT("\\")) + lstrlen(szLocalBufferFile) + 1); pszLocalBuffer = (LPTSTR) VirtualAlloc(NULL, cchLocalBuffer * sizeof(TCHAR), MEM_COMMIT, PAGE_READWRITE); if (NULL == pszLocalBuffer) { hr = E_OUTOFMEMORY; CMTRACE(TEXT("GetRoutesFromURL - VirtualAlloc failed")); goto Cleanup; } if (0 == GetTempPath(cchLocalBuffer, pszLocalBuffer)) { DWORD dwErr = GetLastError(); CMTRACE1(TEXT("GetTempPath 2nd call failed, GLE=%d"), GetLastError()); hr = HRESULT_FROM_WIN32(dwErr); goto Cleanup; } } // // Get a name for the temp file (using the temp path if there is one) // if (0 == GetTempFileName(pszLocalBuffer ? pszLocalBuffer : TEXT("."), TEXT("CMR"), 0, szLocalBufferFile)) { DWORD dwErr = GetLastError(); CMTRACE1(TEXT("GetTempFileName failed, GLE=%d"), dwErr); hr = HRESULT_FROM_WIN32(dwErr); goto Cleanup; } // // Open the temp file, and proceed. // fp = fopen(szLocalBufferFile, "w+b"); if (NULL == fp) { CMTRACE1(TEXT("fopen failed(%s)"), szLocalBufferFile); hr = E_FAIL; goto Cleanup; } // // Initialize WININET // hInternet = InternetOpen(TEXT("RouteMan"), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); if (NULL == hInternet) { hr = HRESULT_FROM_WIN32(GetLastError()); CMTRACE1(TEXT("InternetOpen failed with 0x%x"), hr); goto Cleanup; } // // Supress auto-dial calls to CM from WININET now that we have a handle // SuppressInetAutoDial(hInternet); // // Make sure that WinInet isn't in offline mode // (VOID)SetInetStateConnected(hInternet); // // Open the URL // hPage = InternetOpenUrl(hInternet, pszURL, NULL, 0, 0, 0); if (NULL == hPage) { hr = HRESULT_FROM_WIN32(GetLastError()); CMTRACE1(TEXT("InternetOpenUrl failed with 0x%x"), hr); goto Cleanup; } // // Read the entire URL contents into the tempfile // do { if (!InternetReadFile(hPage, Buffer, sizeof(Buffer), &dwRead)) { hr = HRESULT_FROM_WIN32(GetLastError()); CMTRACE1(TEXT("InternetReadFile failed with 0x%x"), hr); goto Cleanup; } if (fwrite(Buffer, sizeof(BYTE), dwRead, fp) != dwRead) { CMTRACE1(TEXT("write failed to %s"), pszLocalBuffer); hr = HRESULT_FROM_WIN32(ERROR_WRITE_FAULT); goto Cleanup; } cb += dwRead; #if 0 // ISSUE-2000/07/21-SumitC Code seems strange but might need it later // // Vijay/Andrew's original code has this, but is this correct? // The doc for InternetReadFile says this is just an EOF, if we // are to handle this case at all, we should just break; if (!dwRead) goto Cleanup; #endif } while (dwRead == 1024); hr = S_OK; if (fseek(fp, SEEK_SET, 0) != 0) { hr = E_FAIL; goto Cleanup; } pb = (LPBYTE) VirtualAlloc(NULL, cb, MEM_COMMIT, PAGE_READWRITE); if (NULL == pb) { hr = E_OUTOFMEMORY; goto Cleanup; } if (fread(pb, sizeof(BYTE), cb, fp) != cb) { hr = E_FAIL; goto Cleanup; } // success *ppszRouteInfo = (LPSTR) pb; *pcbRouteInfo = cb; Cleanup: if (fp) { fclose(fp); } if (FALSE == fKeepTempFiles) { remove(szLocalBufferFile); } if (pszLocalBuffer && cchLocalBuffer) { VirtualFree(pszLocalBuffer, 0, MEM_RELEASE); } if (hPage) { InternetCloseHandle(hPage); } if (hInternet) { InternetCloseHandle(hInternet); } if (S_OK != hr) { VirtualFree(pb, 0, MEM_RELEASE); } CMTRACEHR("GetRoutesFromURL", hr); return hr; } //+---------------------------------------------------------------------------- // // Func: ProcessRouteInfo // // Desc: Parses the given route table and modifies the real routetable accordingly // // Args: [pszNewRouteInfo] - IN, bytes of route table to parse and add to the real one // [cbNewRouteInfo] - IN, size of routetable // [pmibRouteTable] - IN, real route table // [pGateway] - IN, default gateway // [pfDeleteGateway] - OUT, does the route file say to delete default gateway? // // Return: HRESULT (E_INVALIDARG, E_UNEXPECTED - for html file, etc) // // Notes: // // History: 12-Mar-2000 SumitC Created // //----------------------------------------------------------------------------- HRESULT ProcessRouteInfo( const LPSTR pszNewRouteInfo, DWORD cbNewRouteInfo, PMIB_IPFORWARDTABLE pmibRouteTable, PMIB_IPFORWARDROW pGateway, BOOL * pfDeleteDefaultGateway) { HRESULT hr = S_OK; DWORD cLines = 0; char szBuf[MAX_PATH]; LPSTR pszNextLineToProcess; MYDBGASSERT(pszNewRouteInfo); MYDBGASSERT(cbNewRouteInfo); MYDBGASSERT(pmibRouteTable); MYDBGASSERT(pGateway); MYDBGASSERT(pfDeleteDefaultGateway); if (!pszNewRouteInfo || !cbNewRouteInfo || !pmibRouteTable || !pGateway || !pfDeleteDefaultGateway) { return E_INVALIDARG; } if ((NULL == g_pfnCreateIpForwardEntry) || (NULL == g_pfnDeleteIpForwardEntry)) { return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION); } *pfDeleteDefaultGateway = FALSE; // Make sure WININET zero terminates this.... pszNewRouteInfo[cbNewRouteInfo] = '\0'; // // Convert string to lower // CharLowerA(pszNewRouteInfo); // // sanity checks (in the URL case, if the route file isn't found the server // is likely to return an HTML file to indicate 404 - file not found.) // if (strstr(pszNewRouteInfo, "dwForwardNextHop; } else { if (FALSE == ConvertSzToIP(szBuf, ipGateway)) { CMTRACE2("ProcessRouteInfo [%d] bad format for gateway %s", cLines, szBuf); goto Cleanup; } } // // PART 4 : metric, followed by a number (REQUIRED) // if (FALSE == GetNextToken(psz, &psz, szBuf)) { CMTRACE1("ProcessRouteInfo [%d] didn't find \"metric\" which is required", cLines); goto Cleanup; } if (0 == SafeCompareString(szBuf, "metric")) { if (FALSE == GetNextToken(psz, &psz, szBuf)) { CMTRACE1("ProcessRouteInfo [%d] number value after \"metric\" missing", cLines); goto Cleanup; } if (0 == SafeCompareString(szBuf, "default")) { ipMetric = pGateway->dwForwardMetric1; } else { if (FALSE == ConvertSzToIP(szBuf, ipMetric)) { CMTRACE2("ProcessRouteInfo [%d] required ip metric %s has bad format", cLines, szBuf); goto Cleanup; } /* #if 0 dwParam = sscanf(szBuf, "%d", &ipMetric); if (0 == dwParam) { CMTRACE2("ProcessRouteInfo [%d] bad format for metric value - %s", cLines, szBuf); goto Cleanup; } #endif */ } } else { CMTRACE2("ProcessRouteInfo [%d] found unexpected string %s instead of \"metric\"", cLines, szBuf); goto Cleanup; } // // PART 5 : if (the interface), followed by a number (REQUIRED) // if (FALSE == GetNextToken(psz, &psz, szBuf)) { CMTRACE1("ProcessRouteInfo [%d] didn't find \"if\" which is required", cLines); goto Cleanup; } if (0 == SafeCompareString(szBuf, "if")) { if (FALSE == GetNextToken(psz, &psz, szBuf)) { CMTRACE1("ProcessRouteInfo [%d] number value after \"if\" missing", cLines); goto Cleanup; } if (0 == SafeCompareString(szBuf, "default")) { dwIf = pGateway->dwForwardIfIndex; } else { dwParam = sscanf(szBuf, "%d", &dwIf); if (0 == dwParam) { CMTRACE2("ProcessRouteInfo [%d] bad format for if value - %s", cLines, szBuf); goto Cleanup; } } } else { CMTRACE2("ProcessRouteInfo [%d] found unexpected string %s instead of \"if\"", cLines, szBuf); goto Cleanup; } // // Run the verb (add or delete) // ZeroMemory(&route, sizeof(route)); route.dwForwardDest = ipDest; route.dwForwardIfIndex = dwIf; route.dwForwardMask = ipMask; route.dwForwardMetric1 = ipMetric; route.dwForwardNextHop = ipGateway; route.dwForwardPolicy = 0; route.dwForwardNextHopAS = 0; route.dwForwardType = 3; route.dwForwardProto = 3; route.dwForwardAge = INFINITE; route.dwForwardMetric2 = 0xFFFFFFFF; route.dwForwardMetric3 = 0xFFFFFFFF; route.dwForwardMetric4 = 0xFFFFFFFF; route.dwForwardMetric5 = 0xFFFFFFFF; // ISSUE-2000/07/21-SumitC Can we ever really get here in the code with dwIf == -1 ? // // Check that the interface was specified if (-1 == dwIf) { // Nope, lets go pick one dwIf = GetIf(route, *pmibRouteTable); } DWORD dwRet = 0; switch (eVerb) { case VERB_ADD: dwRet = g_pfnCreateIpForwardEntry(&route); if (ERROR_SUCCESS != dwRet) { // NETWORK_ACCESS_DENIED (0x41=65) is special-cased at the end of SetRoutes CMTRACE2("ProcessRouteInfo [%d] CreateIpForwardEntry failed with %d", cLines, dwRet); hr = HRESULT_FROM_WIN32(dwRet); goto Cleanup; } hr = S_OK; break; case VERB_DELETE: dwRet = g_pfnDeleteIpForwardEntry(&route); if (ERROR_SUCCESS != dwRet) { // NETWORK_ACCESS_DENIED (0x41=65) is special-cased at the end of SetRoutes CMTRACE2("ProcessRouteInfo [%d] DeleteIpForwardEntry failed with %d", cLines, dwRet); hr = HRESULT_FROM_WIN32(dwRet); goto Cleanup; } hr = S_OK; break; default: CMTRACE("ProcessRouteInfo [%d] Unsupported route command, add or delete only"); MYDBGASSERT(0); hr = E_FAIL; break; } } Cleanup: CMTRACEHR("ProcessRouteInfo", hr); return hr; } //+---------------------------------------------------------------------------- // // Func: DeleteDefaultGateway // // Desc: Deletes the default routing gateway for this system // // Args: [pGateway] - the gateway // // Return: HRESULT // // Notes: This should be the last function called within CMroute, and is called // only if all other processing succeeded. // // History: 12-Mar-2000 SumitC Created // //----------------------------------------------------------------------------- HRESULT DeleteDefaultGateway(PMIB_IPFORWARDROW pGateway) { CMTRACE("DeleteDefaultGateway: entering"); HRESULT hr = S_OK; DWORD dwErr = 0; if (NULL == g_pfnDeleteIpForwardEntry) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION); goto Cleanup; } dwErr = g_pfnDeleteIpForwardEntry(pGateway); if (NO_ERROR != dwErr) { hr = HRESULT_FROM_WIN32(dwErr); CMTRACE1("DeleteDefaultGateway failed with error %x", dwErr); } Cleanup: CMTRACEHR("DeleteDefaultGateway", hr); return hr; } //+---------------------------------------------------------------------------- // // Func: IsValidIPAddress // // Desc: Checks to see if given string can be a valid IP address // // Args: [sz] - IN, the string // // Return: BOOL, FALSE means invalid chars were found // // History: 20-Mar-2000 SumitC Created // //----------------------------------------------------------------------------- BOOL IsValidIPAddress(LPSTR sz) { MYDBGASSERT(sz); while ((*sz) && (!isspace(*sz))) { if ((*sz >= '0') && (*sz <= '9')) ; else if ((*sz == '.') || (*sz == '*') || (*sz == '?')) ; else { CMTRACE1(TEXT("IsValidIPAddress failed on %s\n"), sz); MYDBGASSERT("IsValidIPAddress"); return FALSE; } ++sz; } return TRUE; } //+---------------------------------------------------------------------------- // // Func: ConvertSzToIP // // Desc: Converts the given string into a DWORD representing an IP address // // Args: [sz] - IN, string to convert // [dwIP] - OUT BYREF, dword for IP address // // Return: BOOL, FALSE if conversion failed (usually means bad format) // // History: 12-Mar-2000 SumitC Created // //----------------------------------------------------------------------------- BOOL ConvertSzToIP(LPSTR sz, DWORD& dwIP) { DWORD dwParams, d1, d2, d3, d4; if (FALSE == IsValidIPAddress(sz)) { return FALSE; } dwParams = sscanf(sz, "%d.%d.%d.%d", &d1, &d2, &d3, &d4); if (0 == dwParams) { MYDBGASSERT("ConvertSzToIP - bad format for IP address"); return FALSE; } else if (1 == dwParams) { dwIP = d1 | 0xffffff00; } else if (2 == dwParams) { dwIP = d1 | (d2 << 8) | 0xffff0000; } else if (3 == dwParams) { dwIP = d1 | (d2 << 8) | (d3 << 16) | 0xff000000; } else { dwIP = d1 | (d2 << 8) | (d3 << 16) | (d4 << 24); } return TRUE; } //+---------------------------------------------------------------------------- // // Func: GetRouteTable // // Desc: Same as GetIpForwardTable but alloc's the table for you. // VirtualFree() the buffer returned. // // Args: [ppTable] - OUT, returned route table // // Return: HRESULT // // Notes: ppTable should be VirtualFree'd by caller. // // History: 24-Feb-1999 AnBrad Created // 22-Mar-2000 SumitC Rewrote // //----------------------------------------------------------------------------- HRESULT GetRouteTable(PMIB_IPFORWARDTABLE * ppTable) { DWORD dwErr = NO_ERROR; DWORD cbbuf = 0; PMIB_IPFORWARDTABLE p = NULL; HRESULT hr = S_OK; MYDBGASSERT(ppTable); // // Make sure we have a function pointer for GetIpForwardTable // if (NULL == g_pfnGetIpForwardTable) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION); goto Cleanup; } // // Get the route table // dwErr = g_pfnGetIpForwardTable(NULL, &cbbuf, FALSE); if (ERROR_INSUFFICIENT_BUFFER != dwErr) { // hmm, a real error hr = HRESULT_FROM_WIN32(ERROR_UNEXP_NET_ERR); goto Cleanup; } else { p = (PMIB_IPFORWARDTABLE) VirtualAlloc(NULL, cbbuf, MEM_COMMIT, PAGE_READWRITE); if (!p) { hr = E_OUTOFMEMORY; goto Cleanup; } if (g_pfnGetIpForwardTable(p, &cbbuf, TRUE)) { hr = E_FAIL; goto Cleanup; } *ppTable = p; } Cleanup: if (S_OK != hr) { if (p) { VirtualFree(p, 0, MEM_RELEASE); } } CMTRACEHR("GetRouteTable", hr); return hr; } //+---------------------------------------------------------------------------- // // Func: GetIf // // Desc: Find the interface for a route // // Args: [route] - IN, route for which we need the Interface // [RouteTable] - IN, route table // // Return: DWORD which is the IF // // Notes: Logic stolen from \nt\private\net\sockets\strmtcp\route // // History: 24-Feb-1999 AnBrad Created // 22-Mar-2000 SumitC Cleaned up // //----------------------------------------------------------------------------- DWORD GetIf(const MIB_IPFORWARDROW& route, const MIB_IPFORWARDTABLE& RouteTable) { for(DWORD dwIndex = 0; dwIndex < RouteTable.dwNumEntries; dwIndex++) { const MIB_IPFORWARDROW& Row = RouteTable.table[dwIndex]; if (Row.dwForwardMask && (Row.dwForwardDest & Row.dwForwardMask) == (route.dwForwardNextHop & Row.dwForwardMask)) { return Row.dwForwardIfIndex; } } return 0; } //+---------------------------------------------------------------------------- // // Func: GetDefaultGateway // // Desc: Find the default gateway // // Args: [pRouteTable] - IN, the route table (IP forward table) // // Return: PMIB_IPFORWARDROW the row of the gateway (a ptr within pRouteTable) // // Notes: Do not free returned value // // History: 24-Feb-1999 AnBrad Created // 22-Mar-2000 SumitC Cleaned up // //----------------------------------------------------------------------------- PMIB_IPFORWARDROW GetDefaultGateway(PMIB_IPFORWARDTABLE pRouteTable) { PMIB_IPFORWARDROW pRow, pDefGateway; // Cycle thru the entire table & find the gateway with the least metric pDefGateway = NULL; for(pRow = pRouteTable->table; pRow != pRouteTable->table + pRouteTable->dwNumEntries; ++pRow) { if (pRow->dwForwardDest == 0) { if (pDefGateway == NULL) { pDefGateway = pRow; } else { if (pRow->dwForwardMetric1 == pDefGateway->dwForwardMetric1 && pRow->dwForwardAge >= pDefGateway->dwForwardAge) { pDefGateway = pRow; } if (pRow->dwForwardMetric1 < pDefGateway->dwForwardMetric1) { pDefGateway = pRow; } } } } return pDefGateway; } //+---------------------------------------------------------------------------- // // Func: StrCpyWithoutQuotes // // Desc: Wrapper for lstrcpy, which removes surrounding double quotes if necessary // // Args: [pszDest] - OUT, destination for the copy // [pszSrc] - OUT, source for the copy // // Return: a ptr to pszDest, or NULL if failure. // // Notes: // // History: 12-Apr-1999 SumitC Created // //----------------------------------------------------------------------------- LPSTR StrCpyWithoutQuotes(LPSTR pszDest, LPCSTR pszSrc) { MYDBGASSERT(pszDest); MYDBGASSERT(pszSrc); int len = lstrlen(pszSrc); if ((len > 2) && ('"' == pszSrc[0]) && ('"' == pszSrc[len - 1])) { return lstrcpyn(pszDest, &pszSrc[1], len - 1); } else { return lstrcpy(pszDest, pszSrc); } } //+---------------------------------------------------------------------------- // // Func: VerifyProfileAndGetServiceDir // // Desc: Checks the given profile, and modifies it to produce the ServiceDir // // Args: [pszProfile] - IN OUT, profile name, modified IN PLACE // // Return: TRUE if modified pszProfile is now the ServiceDir, or FALSE if failure. // // Notes: pszProfile is MODIFIED IN PLACE. // // History: 12-Apr-1999 SumitC Created // //----------------------------------------------------------------------------- BOOL VerifyProfileAndGetServiceDir(IN OUT LPSTR pszProfile) { HANDLE hFile = NULL; MYDBGASSERT(pszProfile); MYDBGASSERT(lstrlen(pszProfile)); if ((NULL == pszProfile) || (lstrlen(pszProfile) < 4)) { return FALSE; } // // The profile string may be surrounded by double-quotes, if so remove them. // // // First check to see if the profile really exists. This also serves as // verification for the existence of the directory. // hFile = CreateFile(pszProfile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hFile) { return FALSE; } else { CloseHandle(hFile); } // // Now check to see that the file does indeed end in .CMP // LPSTR psz = pszProfile + lstrlen(pszProfile) - lstrlen(".CMP"); if (0 != SafeCompareString(psz, ".CMP")) { return FALSE; } // // The profile name is the same as the ServiceDir name, so we just terminate // the string at the '.' and append a '\' // *psz = '\\'; psz++; *psz = '\0'; return TRUE; } #if DBG LPTSTR aszMapType[] = {TEXT("Other"), TEXT("Invalid"), TEXT("Direct"), TEXT("Indirect")}; LPTSTR aszMapProto[] = {TEXT("Other"), // MIB_IPPROTO_OTHER 1 TEXT("Local"), // MIB_IPPROTO_LOCAL 2 TEXT("SNMP"), // MIB_IPPROTO_NETMGMT 3 TEXT("ICMP"), // MIB_IPPROTO_ICMP 4 TEXT("Exterior Gateway Protocol"), // MIB_IPPROTO_EGP 5 TEXT("GGP"), // MIB_IPPROTO_GGP 6 TEXT("Hello"), // MIB_IPPROTO_HELLO 7 TEXT("Routing Information Protocol"), // MIB_IPPROTO_RIP 8 TEXT("IS IS"), // MIB_IPPROTO_IS_IS 9 TEXT("ES IS"), // MIB_IPPROTO_ES_IS 10 TEXT("Cicso"), // MIB_IPPROTO_CISCO 11 TEXT("BBN"), // MIB_IPPROTO_BBN 12 TEXT("Open Shortest Path First"), // MIB_IPPROTO_OSPF 13 TEXT("Border Gateway Protocol")}; // MIB_IPPROTO_BGP 14 //+---------------------------------------------------------------------- // // Function: PrintRoute // // Purpose: Prints out a route from the IP forward table // // Arguments: // pRow [in] the route // // Returns: zip // // Author: anbrad 24 Feb 1999 // // Notes: // //----------------------------------------------------------------------- void PrintRoute(PMIB_IPFORWARDROW pRow) { TCHAR sz[MAX_PATH]; wsprintf(sz, "dwDest = %s\n", IPtoTsz(pRow->dwForwardDest)); // IP addr of destination OutputDebugString(sz); wsprintf(sz, "dwMask = %s\n", IPtoTsz(pRow->dwForwardMask)); // subnetwork mask of destination OutputDebugString(sz); wsprintf(sz, "dwPolicy = %d\n" "dwNextHop = %s\n" "dwIfIndex = %d\n" "dwType = %s\n" "dwProto = %s\n" "dwAge = %d\n" "dwNextHopAS = %d\n", pRow->dwForwardPolicy, // conditions for multi-path route IPtoTsz(pRow->dwForwardNextHop), // IP address of next hop pRow->dwForwardIfIndex, // index of interface aszMapType[pRow->dwForwardType-1], // route type aszMapProto[pRow->dwForwardProto-1],// protocol that generated route pRow->dwForwardAge, // age of route pRow->dwForwardNextHopAS); // autonomous system number // of next hop OutputDebugString(sz); if (MIB_IPROUTE_METRIC_UNUSED != pRow->dwForwardMetric1) { wsprintf(sz, "dwMetric1 = %d\n", pRow->dwForwardMetric1); OutputDebugString(sz); } if (MIB_IPROUTE_METRIC_UNUSED != pRow->dwForwardMetric2) { wsprintf(sz, "dwMetric2 = %d\n", pRow->dwForwardMetric2); OutputDebugString(sz); } if (MIB_IPROUTE_METRIC_UNUSED != pRow->dwForwardMetric3) { wsprintf(sz, "dwMetric3 = %d\n", pRow->dwForwardMetric3); OutputDebugString(sz); } if (MIB_IPROUTE_METRIC_UNUSED != pRow->dwForwardMetric4) { wsprintf(sz, "dwMetric4 = %d\n", pRow->dwForwardMetric4); OutputDebugString(sz); } if (MIB_IPROUTE_METRIC_UNUSED != pRow->dwForwardMetric5) { wsprintf(sz, "dwMetric5 = %d\n", pRow->dwForwardMetric5); OutputDebugString(sz); } wsprintf(sz, "\n"); OutputDebugString(sz); } #if DBG //+---------------------------------------------------------------------- // // Function: PrintRouteTable // // Purpose: Does a "ROUTE PRINT" using the iphlpapi's // // Arguments: none // // Returns: zip // // Author: anbrad 24 Feb 1999 // // Notes: // //----------------------------------------------------------------------- void PrintRouteTable() { #define PAGE 4096 BYTE buf[PAGE]; DWORD cbbuf = sizeof(buf); TCHAR sz[MAX_PATH]; if (g_pfnGetIpForwardTable) { PMIB_IPFORWARDTABLE table = (PMIB_IPFORWARDTABLE)buf; if (g_pfnGetIpForwardTable(table, &cbbuf, TRUE)) return; wsprintf(sz, "\n\nFound %d routes\n", table->dwNumEntries); OutputDebugString(sz); for (DWORD d=0; d < table->dwNumEntries; ++d) { PrintRoute(table->table+d); } } } #endif //+---------------------------------------------------------------------- // // Function: IPtoTsz // // Purpose: Changes a dword to a "dotted string" notation // // Arguments: // dw [in] IP address // // Returns: LPTSTR which is a static string // // Author: anbrad 24 Feb 1999 // // Notes: Global makes this NOT thread safe, and it is not needed // for cmroute.exe // //----------------------------------------------------------------------- TCHAR tsz[20]; LPTSTR IPtoTsz(DWORD dw) { wsprintf(tsz, TEXT("%03d.%03d.%03d.%03d"), (DWORD)LOBYTE(LOWORD(dw)), (DWORD)HIBYTE(LOWORD(dw)), (DWORD)LOBYTE(HIWORD(dw)), (DWORD)HIBYTE(HIWORD(dw))); return tsz; } LPSTR IPtosz(DWORD dwIP, char *psz) { wsprintfA(psz, ("%d.%d.%d.%d"), (DWORD)LOBYTE(LOWORD(dwIP)), (DWORD)HIBYTE(LOWORD(dwIP)), (DWORD)LOBYTE(HIWORD(dwIP)), (DWORD)HIBYTE(HIWORD(dwIP))); return psz; } #endif // DBG