netchck.cpp
上传用户:dangjiwu
上传日期:2013-07-19
资源大小:42019k
文件大小:37k
源码类别:

Symbian

开发平台:

Visual C++

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Source last modified: $Id: netchck.cpp,v 1.5.46.2 2004/07/09 02:07:40 hubbe Exp $
  3.  * 
  4.  * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
  5.  * 
  6.  * The contents of this file, and the files included with this file,
  7.  * are subject to the current version of the RealNetworks Public
  8.  * Source License (the "RPSL") available at
  9.  * http://www.helixcommunity.org/content/rpsl unless you have licensed
  10.  * the file under the current version of the RealNetworks Community
  11.  * Source License (the "RCSL") available at
  12.  * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
  13.  * will apply. You may also obtain the license terms directly from
  14.  * RealNetworks.  You may not use this file except in compliance with
  15.  * the RPSL or, if you have a valid RCSL with RealNetworks applicable
  16.  * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
  17.  * the rights, obligations and limitations governing use of the
  18.  * contents of the file.
  19.  * 
  20.  * Alternatively, the contents of this file may be used under the
  21.  * terms of the GNU General Public License Version 2 or later (the
  22.  * "GPL") in which case the provisions of the GPL are applicable
  23.  * instead of those above. If you wish to allow use of your version of
  24.  * this file only under the terms of the GPL, and not to allow others
  25.  * to use your version of this file under the terms of either the RPSL
  26.  * or RCSL, indicate your decision by deleting the provisions above
  27.  * and replace them with the notice and other provisions required by
  28.  * the GPL. If you do not delete the provisions above, a recipient may
  29.  * use your version of this file under the terms of any one of the
  30.  * RPSL, the RCSL or the GPL.
  31.  * 
  32.  * This file is part of the Helix DNA Technology. RealNetworks is the
  33.  * developer of the Original Code and owns the copyrights in the
  34.  * portions it created.
  35.  * 
  36.  * This file, and the files included with this file, is distributed
  37.  * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
  38.  * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
  39.  * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
  40.  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
  41.  * ENJOYMENT OR NON-INFRINGEMENT.
  42.  * 
  43.  * Technology Compatibility Kit Test Suite(s) Location:
  44.  *    http://www.helixcommunity.org/content/tck
  45.  * 
  46.  * Contributor(s):
  47.  * 
  48.  * ***** END LICENSE BLOCK ***** */
  49. #include "hlxclib/windows.h"
  50. #include "prefdefs.h"
  51. #include "netchck.h"
  52. #include "hxtick.h"
  53. #include "hxassert.h"
  54. #include "hxver.h"
  55. #include <raserror.h>     // for ras error codes
  56. #define DEF_DNS_PORT      53 //tcp
  57. #define DIALOG_CLASS "#32770"
  58. #include "hxheap.h"
  59. #ifdef _DEBUG
  60. #undef HX_THIS_FILE
  61. static const char HX_THIS_FILE[] = __FILE__;
  62. #endif
  63. // Comment out this definition for release versions that shouldn't have
  64. // logging code compiled in.
  65. #ifndef _WINCE
  66. #define USE_NETDETECT_LOG 1
  67. #endif //_WINCE
  68. #ifdef USE_NETDETECT_LOG
  69.     #include "hlxclib/fcntl.h"     // for file constants
  70.     #include "chxdataf.h"     // CHXDataFile
  71.     #include "hlxclib/time.h"
  72.     #include "pref.h"     // CPref
  73.     #include "ihxpckts.h"   // IHXBuffer
  74. #endif
  75. namespace NetDetectLog
  76. {
  77.     #ifndef USE_NETDETECT_LOG
  78. #ifndef _WINCE
  79. #define WRITE_LOG0(format) do {;} while(0)
  80. #define WRITE_LOG1(format, x1) do {;} while(0)
  81. #define WRITE_LOG_WINET(result, flags) do {;} while(0)
  82. #else  //_WINCE
  83. #define WRITE_LOG0(format) HX_TRACE(format)
  84. #define WRITE_LOG1(format, x1) HX_TRACE(format, x1)
  85. #define WRITE_LOG_WINET(result, flags) HX_TRACE("InternetGetConnectedState: result=%d, flags=%d",(int)result, (int)flags)
  86. #endif //_WINCE
  87. #define INITIALIZE_LOG() do {;} while(0)
  88. #define CLOSE_LOG() do {;} while(0)
  89.     #else
  90. #define STR_EOL "rn"
  91. const char* const kszNetdetectLogFileName = "netdtlog.txt";
  92. const char* const kszNetdetectLogPrefKey = "netdetectlog";
  93. static CHXDataFile* g_pLogFile = NULL;
  94. HX_RESULT InitializeLog();
  95. HX_RESULT CloseLog();
  96. HX_RESULT WriteToLog(const CHXString& msg);
  97. HX_RESULT WriteToLogWinInetGetConnected(BOOL result, DWORD flags);
  98. #define INITIALIZE_LOG() do { InitializeLog(); } while(0)
  99. #define CLOSE_LOG() do { CloseLog(); } while(0)
  100. #define WRITE_LOG0(format)
  101.     WriteToLog(format);
  102. #define WRITE_LOG1(format, x1){
  103.     CHXString strFormat;
  104.     strFormat.Format(format, x1);
  105.     WriteToLog(strFormat);}
  106. #define WRITE_LOG_WINET(result, flags)
  107.     WriteToLogWinInetGetConnected(result, flags)
  108. HX_RESULT WriteToLogWinInetGetConnected(BOOL result, DWORD flags)
  109. {
  110.     // Flags for InternetGetConnectedState 
  111.     #define INTERNET_CONNECTION_MODEM           0x01
  112.     #define INTERNET_CONNECTION_LAN             0x02
  113.     #define INTERNET_CONNECTION_PROXY           0x04
  114.     #define INTERNET_CONNECTION_MODEM_BUSY      0x08  /* no longer used */
  115.     #define INTERNET_RAS_INSTALLED              0x10
  116.     #define INTERNET_CONNECTION_OFFLINE         0x20
  117.     #define INTERNET_CONNECTION_CONFIGURED      0x40
  118.     CHXString strFormat;
  119.     if(result) 
  120.     {
  121. CHXString format("InternetGetConnectedState returned TRUE; flag = %s");
  122. if(flags & INTERNET_CONNECTION_MODEM)
  123. {
  124.     strFormat.Format(format, "INTERNET_CONNECTION_MODEM");
  125. }
  126. else if(flags & INTERNET_CONNECTION_LAN)
  127. {
  128.     strFormat.Format(format, "INTERNET_CONNECTION_LAN");
  129. }
  130. else if(flags & INTERNET_CONNECTION_PROXY)
  131. {
  132.     strFormat.Format(format, "INTERNET_CONNECTION_PROXY");
  133. }
  134. else if(flags & INTERNET_CONNECTION_MODEM_BUSY)
  135. {
  136.     strFormat.Format(format, "INTERNET_CONNECTION_MODEM_BUSY");
  137. }
  138. else if(flags & INTERNET_RAS_INSTALLED)
  139. {
  140.     strFormat.Format(format, "INTERNET_RAS_INSTALLED");
  141. }
  142. else if(flags & INTERNET_CONNECTION_OFFLINE)
  143. {
  144.     strFormat.Format(format, "INTERNET_CONNECTION_OFFLINE");
  145. }
  146. else if(flags & INTERNET_CONNECTION_CONFIGURED)
  147. {
  148.     strFormat.Format(format, "INTERNET_CONNECTION_CONFIGURED");
  149. }
  150.     }
  151.     else
  152.     {
  153. strFormat = "InternetGetConnectedState returned FALSE";
  154.     }
  155.     return WriteToLog(strFormat);
  156. }
  157. HX_RESULT InitializeLog()
  158. {
  159.     HX_RESULT res = HXR_FAIL;
  160.     BOOL bShouldWriteLogFile = FALSE;
  161.     // Read from the preferences to see if we should write a log file.
  162.     CPref* pPreferences = CPref::open_shared_pref( HXVER_COMMUNITY );
  163.     if( pPreferences )
  164.     {
  165. IHXBuffer* pBuffer = NULL;
  166. if( SUCCEEDED( pPreferences->read_pref( kszNetdetectLogPrefKey, pBuffer ))) 
  167. {
  168.     bShouldWriteLogFile = 
  169. ( atoi( reinterpret_cast<const char*>(pBuffer->GetBuffer())) == 1 );
  170.     HX_RELEASE(pBuffer);
  171. }
  172. HX_DELETE( pPreferences );
  173.     }
  174.     if( !bShouldWriteLogFile )
  175.     {
  176. return res;
  177.     }
  178.     // First try to open the file for appending.  If that doesn't work,
  179.     // then just create a new one.
  180.     g_pLogFile = CHXDataFile::Construct();
  181.     if (g_pLogFile)
  182.     {
  183. res = g_pLogFile->Open(kszNetdetectLogFileName, 
  184.     O_BINARY | O_WRONLY | O_APPEND, TRUE);
  185. if (FAILED(res))
  186. {
  187.     res = g_pLogFile->Open(kszNetdetectLogFileName, 
  188. O_BINARY | O_CREAT | O_TRUNC | O_WRONLY, TRUE);
  189. }
  190. if( SUCCEEDED( res ))
  191. {
  192.     CHXString strMsg;
  193.     strMsg.Format("%s################################################"
  194. "##############################%s", 
  195. STR_EOL, STR_EOL);
  196.     g_pLogFile->Write(strMsg, strMsg.GetLength());
  197. }
  198. else
  199. {
  200.     HX_DELETE(g_pLogFile);
  201. }
  202.     }
  203.     return res;
  204. }
  205. HX_RESULT CloseLog()
  206. {
  207.     if (g_pLogFile)
  208.     {
  209. g_pLogFile->Close();
  210. HX_DELETE(g_pLogFile);
  211.     }
  212.     return HXR_OK;
  213. }
  214. HX_RESULT WriteToLog(const CHXString& msg)
  215. {
  216.     HX_RESULT res = HXR_FAIL;
  217.     if (g_pLogFile)
  218.     {
  219. time_t now = time(NULL);
  220. struct tm* date = localtime(&now);
  221. char szTime[20] = { 0 }; /* Flawfinder: ignore */
  222. #if !defined(WIN32_PLATFORM_PSPC)
  223. if(date)
  224. {
  225.     strftime(szTime, 19, "%X", date);
  226. }
  227. #endif /* !defined(WIN32_PLATFORM_PSPC) */
  228. CHXString newMsg;
  229. newMsg.Format("%s: %s%s", szTime, (const char*)msg, STR_EOL);
  230. res = (g_pLogFile->Write(newMsg, newMsg.GetLength()) 
  231.     == newMsg.GetLength()) ? HXR_OK : HXR_FAIL;
  232.     }
  233.     return res;
  234. }
  235.     #endif // USE_NETDETECT_LOG
  236.     // "resource aquisition is init" - Init/ Close log file
  237.     class NetDetectLogLib
  238.     {
  239.     public:
  240. NetDetectLogLib() { INITIALIZE_LOG(); }
  241. ~NetDetectLogLib() { CLOSE_LOG(); }
  242.     };
  243. } //NetDetectLog namespace 
  244. CHXNetCheck::CHXNetCheck(UINT32 timeout) : 
  245. XHXNetCheck(timeout)
  246. ,m_pRmaNetServices(0)
  247. , m_pRmaTCPSocket(0)
  248. , m_pContext(0)
  249. , m_fConnected(FALSE)
  250. , m_fFailed(FALSE)
  251. , m_lRefCount(0)
  252. #ifndef _WIN16
  253. , m_hRasApiModule(0)
  254. , m_pRasEnumConnections(0)
  255. #endif
  256. {
  257. }
  258. CHXNetCheck::~CHXNetCheck()
  259. {
  260. if (m_pContext)
  261. m_pContext->Release();
  262. m_pContext = NULL;
  263. if (m_pRmaNetServices)
  264. m_pRmaNetServices->Release();
  265. m_pRmaNetServices = NULL;
  266. if (m_pRmaTCPSocket)
  267. m_pRmaTCPSocket->Release();
  268. m_pRmaTCPSocket = NULL;
  269. }
  270. /////////////////////////////////////////////////////////////////////////
  271. //  Method:
  272. // IUnknown::QueryInterface
  273. //  Purpose:
  274. // Implement this to export the interfaces supported by your 
  275. // object.
  276. //
  277. STDMETHODIMP CHXNetCheck::QueryInterface(REFIID riid, void** ppvObj)
  278. {
  279.     if (IsEqualIID(riid, IID_IUnknown))
  280.     {
  281.     AddRef();
  282.     *ppvObj = this;
  283.     return HXR_OK;
  284.     }
  285.     if (IsEqualIID(riid, IID_IHXTCPResponse))
  286.     {
  287.     AddRef();
  288.     *ppvObj = (IHXTCPResponse*)this;
  289.     return HXR_OK;
  290.     }
  291.     *ppvObj = NULL;
  292.     return HXR_NOINTERFACE;
  293. }
  294. /////////////////////////////////////////////////////////////////////////
  295. //  Method:
  296. // IUnknown::AddRef
  297. //  Purpose:
  298. // Everyone usually implements this the same... feel free to use
  299. // this implementation.
  300. //
  301. STDMETHODIMP_(UINT32) CHXNetCheck::AddRef()
  302. {
  303.     return InterlockedIncrement(&m_lRefCount);
  304. }
  305. /////////////////////////////////////////////////////////////////////////
  306. //  Method:
  307. // IUnknown::Release
  308. //  Purpose:
  309. // Everyone usually implements this the same... feel free to use
  310. // this implementation.
  311. //
  312. STDMETHODIMP_(UINT32) CHXNetCheck::Release()
  313. {
  314.     if (InterlockedDecrement(&m_lRefCount) > 0)
  315.     {
  316.         return m_lRefCount;
  317.     }
  318.     delete this;
  319.     return 0;
  320. }
  321. /*
  322.  * IHXTCPResponse methods
  323.  */
  324. /************************************************************************
  325.  * Method:
  326.  *     IHXTCPResponse::ConnectDone
  327.  * Purpose:
  328.  *     A Connect operation has been completed or an error has occurred.
  329.  */
  330. STDMETHODIMP CHXNetCheck::ConnectDone (HX_RESULT status)
  331. {
  332. HX_ASSERT(m_fConnected == FALSE); // We shouldn't be getting called if 
  333. // we aren't expecting it.
  334. if (status == HXR_OK)
  335. m_fConnected = TRUE;
  336. else
  337. m_fFailed = TRUE;
  338. return HXR_OK;
  339. }
  340. /************************************************************************
  341.  * Method:
  342.  *     IHXTCPResponse::ReadDone
  343.  * Purpose:
  344.  *     A Read operation has been completed or an error has occurred.
  345.  *     The data is returned in the IHXBuffer.
  346.  */
  347. STDMETHODIMP CHXNetCheck::ReadDone (HX_RESULT status,
  348. IHXBuffer* pBuffer)
  349. {
  350. HX_ASSERT(FALSE);
  351. return HXR_OK;
  352. }
  353. /************************************************************************
  354.  * Method:
  355.  *     IHXTCPResponse::WriteReady
  356.  * Purpose:
  357.  *     This is the response method for WantWrite.
  358.  *     If HX_RESULT is ok, then the TCP channel is ok to Write to.
  359.  */
  360. STDMETHODIMP CHXNetCheck::WriteReady (HX_RESULT status)
  361. {
  362. HX_ASSERT(FALSE);
  363. return HXR_OK;
  364. }
  365. /************************************************************************
  366.  * Method:
  367.  *     IHXTCPResponse::Closed
  368.  * Purpose:
  369.  *     This method is called to inform you that the TCP channel has
  370.  *     been closed by the peer or closed due to error.
  371.  */
  372. STDMETHODIMP CHXNetCheck::Closed(HX_RESULT status)
  373. {
  374. m_fConnected = FALSE;
  375. return HXR_OK;
  376. }
  377. //******************************************************************************
  378. //
  379. // Method: CHXNetCheck::Init
  380. //
  381. // Purpose: Sets up the CHXNetCheck object with a context.
  382. //
  383. //
  384. // Notes: n/a
  385. //
  386. //******************************************************************************
  387. HX_RESULT 
  388. CHXNetCheck::Init(IUnknown *pContext)
  389. {
  390. HX_RESULT result = HXR_OK;
  391. if (!pContext)
  392. return HXR_FAILED;
  393. m_pContext = pContext;
  394. m_pContext->AddRef();
  395. return result;
  396. }
  397. //******************************************************************************
  398. //
  399. // Method: CHXNetCheck::FInternetAvailable
  400. //
  401. // Purpose: The function checks if we can attempt a connection without 
  402. //     being prompted to connect.  
  403. // Returns true if we can, false otherwise.
  404. //
  405. // Notes: There is no single function for determining if a machine is 
  406. // connected to the internet, and it is impossible to reliably 
  407. // determine what is happening without side effects - such as 
  408. // automatic network connections taking place.
  409. //
  410. // MSDN article - 
  411. // "HOWTO: Detecting If you have a Connection to the Internet"
  412. //
  413. // "Usually the best way to determine if you have a connection 
  414. // to a particular computer is to attempt the connection. If the 
  415. // autodial feature of Windows is enabled then attempting the 
  416. // connection may cause the default Internet dialup connectoid 
  417. // to be opened, and you will be prompted with your credentials to connect.
  418. //
  419. // To avoid having the default Internet connectoid dialed, 
  420. // the InternetGetConnectedState function can be used to determine 
  421. // if there is a default Internet dialup connectoid configured and 
  422. // whether it is currently active or not. If there is a default 
  423. // Internet dialup connectoid configured and it is not currently 
  424. // active then InternetGetConnectedState will return FALSE. 
  425. // If InternetGetConnectedState returns TRUE then you can attempt 
  426. // to connect to the Internet resource without fear of being prompted 
  427. // to connect to another Internet Service Provider.
  428. //
  429. // You cannot rely solely on the fact that InternetGetConnectedState 
  430. // returning TRUE means that you have a valid active Internet 
  431. // connection. It is impossible for InternetGetConnectedState 
  432. // to determine if the entire connection to the Internet is 
  433. // functioning without sending a request to a server. This is why 
  434. // you need to send a request to determine if you are really connected 
  435. // or not. You can be assured however that if InternetGetConnectedState 
  436. // returns TRUE, that attempting your connection will NOT cause you 
  437. // to be prompted to connect to the default Internet Service Provider.
  438. //
  439. // Be aware that InternetGetConnectedState only reports the status 
  440. // of the default Internet connectoid on Internet Explorer 4.x. 
  441. // If a nondefault connectoid is connected, InternetGetConnectedState 
  442. // always returns FALSE (unless a LAN connection is used). 
  443. // With Internet Explorer 4.x configured to use a LAN connection, 
  444. // InternetGetConectedState always returns TRUE.
  445. //
  446. // Internet Explorer 5 behaves differently. If you are currently dialed 
  447. // into a different dial-up in Internet Explorer 5, InternetGetConnectedState 
  448. // reports dial-up connection status as long as any connectoid is dialed 
  449. // or an active LAN connection exists." 
  450. //
  451. //******************************************************************************
  452. BOOL 
  453. CHXNetCheck::FInternetAvailable(BOOL fPing, BOOL fProxy)
  454. {
  455. using namespace NetDetectLog; 
  456. NetDetectLogLib logLib;
  457. BOOL fRet = FALSE;
  458. UINT16 nPort = DEF_HTTP_PORT;
  459. #ifndef _WIN16
  460. WRITE_LOG0("FInternetAvailable fc entered...");
  461. HKEY hKey = 0 ;
  462. DWORD fAutoDial = 0 ;
  463. DWORD regType = 0 ;
  464. DWORD cbBuf;
  465. #if !defined(WIN32_PLATFORM_PSPC)
  466. // set the error mode since we'll do some DLL loading and we want the OS to be
  467. // quiet whhen we do this
  468. UINT oldErrorMode = ::SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
  469. #endif /* !defined(WIN32_PLATFORM_PSPC) */
  470. // XXXSO - agreed to use InternetGetConnectedState function only.
  471. // The rest of the code should be called when this function is not available.
  472. BOOL fConnected = FALSE;
  473. if( SUCCEEDED( WinInetGetConnected( fConnected )))
  474. {
  475.     fRet = fPing ? SmartPing() : fConnected;
  476.     goto Ret;
  477. }
  478. // XXXKM - agreed to remove proxy impact on net detect; instead let the following code determine
  479. // if we are connected or not - doing so will alleviate issues with proxy and dialup
  480. #if 0
  481. if (fProxy)
  482.     return TRUE;
  483. #endif
  484. WRITE_LOG0("Old algorithm entered ... ");
  485. WRITE_LOG0("Checking autodial flag ...");
  486. // XXXKM - check this early; if autodial is set, then return FALSE temporarily
  487. // check the autodial bit in the registry.
  488. cbBuf = sizeof(fAutoDial);
  489. if (RegOpenKey(HKEY_CURRENT_USER, OS_STRING("Software\Microsoft\Windows\CurrentVersion\Internet Settings"),
  490. &hKey) == ERROR_SUCCESS)
  491. {
  492.     OSVERSIONINFO osv;
  493.     memset(&osv, 0, sizeof(osv));
  494.     osv.dwOSVersionInfoSize = sizeof(osv);
  495.     GetVersionEx(&osv);
  496.     if (osv.dwPlatformId != VER_PLATFORM_WIN32_NT)
  497.     {
  498. WRITE_LOG0("Checking Win95/98 EnableAutodial flag in HKCU/Software/Microsoft/Windows"
  499.     "/Current Version/Internet Settings...");
  500. // check for the Win95/98 flag
  501. RegQueryValueEx(hKey, OS_STRING("EnableAutodial"), NULL, &regType, (BYTE *) &fAutoDial, &cbBuf);
  502.     }
  503.     else
  504.     {
  505. WRITE_LOG0("Checking for the RAS key on NT.");
  506. // XXXKM - check for the RAS key on NT; only way I could find around the assert/crash in rasapi32.dll
  507. // in situations where the dll existed, but ras was disabled/removed on the machine
  508. HKEY hRasKey = 0;
  509. if (::RegOpenKey(HKEY_LOCAL_MACHINE, OS_STRING("SOFTWARE\Microsoft\RAS"), &hRasKey) == ERROR_SUCCESS)
  510. {
  511.     // close the ras key
  512.     ::RegCloseKey(hRasKey);
  513.     // initialize to FALSE so this works the way it always has if something doesn't go right
  514.     fAutoDial = FALSE;
  515.     if (!m_hRasApiModule)
  516. m_hRasApiModule = ::LoadLibrary(OS_STRING("rasapi32.dll"));
  517.     if (m_hRasApiModule)
  518.     {
  519. WRITE_LOG0("rasapi32.dll loaded...");
  520. // try and get the API to enumerate RAS devices
  521. FPRASENUMDEVICES fpRasEnumDevices = (FPRASENUMDEVICES)GetProcAddress(m_hRasApiModule,
  522.     OS_STRING("RasEnumDevicesA"));
  523. if (!fpRasEnumDevices)
  524. {
  525.     fpRasEnumDevices  = (FPRASENUMDEVICES)GetProcAddress(m_hRasApiModule,
  526.     OS_STRING("RasEnumDevicesW"));
  527. }
  528. // we use this to see if there is a RAS enabled device on this system and also to 
  529. // check for initialization of the RAS subsystem since it will return an error if 
  530. // RAS is disabled or fails to initialize for whatever reason
  531. // XXXKM - this was added to prevent crashing on Win NT systems in the EnumEntries call
  532. // on machines where RAS has been disabled due to hardware profiles
  533. if (fpRasEnumDevices)
  534. {
  535.     WRITE_LOG0("Checking the RAS enabled devices...");
  536.     
  537.     RASDEVINFO rasDevInfo[1];
  538.     rasDevInfo[0].dwSize = sizeof(rasDevInfo);
  539.     DWORD bufferSize = sizeof(rasDevInfo);
  540.     DWORD numEntries = 0;
  541.     DWORD retVal = fpRasEnumDevices(rasDevInfo, &bufferSize, &numEntries);
  542.     // check if there was actually a RAS enabled device; this API will return an error
  543.     // if there are no RAS devices enabled, or if the RAS subsystem couldn't be initialized
  544.     if (retVal == ERROR_BUFFER_TOO_SMALL || retVal == ERROR_SUCCESS && numEntries == 1)
  545.     {
  546. // find the right entrypoint in the rasapi32.dll
  547. FPRASENUMENTRIES fpRasEnumEntries = (FPRASENUMENTRIES)GetProcAddress(m_hRasApiModule,
  548.     OS_STRING("RasEnumEntriesA"));
  549. if (!fpRasEnumEntries)
  550. {
  551.     fpRasEnumEntries = (FPRASENUMENTRIES)GetProcAddress(m_hRasApiModule,
  552.     OS_STRING("RasEnumEntriesW"));
  553. }
  554. // if we've got the RAS entries function, then call it
  555. if (fpRasEnumEntries)
  556. {
  557.     WRITE_LOG0("Calling RasEnumEntries function...");
  558.     
  559.     // setup the RAS entry structure for a single dialup item
  560.     RASENTRYNAME rasEntryName[1];
  561.     rasEntryName[0].dwSize = sizeof(rasEntryName);
  562.     bufferSize = sizeof(rasEntryName);
  563.     numEntries = 0;
  564.     // call the function to see how many dialup entries there are
  565.     retVal = fpRasEnumEntries(NULL, NULL, rasEntryName, &bufferSize, &numEntries);
  566.     // if our buffer is too small (i.e. there are more than one phone book entries)
  567.     // or we get a single item, then we have dialup, so set autodial to TRUE so 
  568.     // we'll tread lightly
  569.     if (retVal == ERROR_BUFFER_TOO_SMALL || (numEntries == 1 && retVal == ERROR_SUCCESS))
  570. fAutoDial = TRUE;
  571. }
  572.     }
  573. }
  574.     }
  575. }
  576.     }
  577.     // close the key when we're done with it
  578.     ::RegCloseKey(hKey);
  579. }
  580. WRITE_LOG1("Checked AUTODIAL flag = %d.", fAutoDial);
  581. // XXXKM - only check netcard active status if autodial is not on; if it's on, then
  582. // assume we are doing stuff through RAS connection only and ignore netcard for now;
  583. // only temporary
  584. if (!fAutoDial && FNetCardActive())
  585. {
  586.     WRITE_LOG0("FNetCardActive fc returned TRUE. goto exit ...");
  587.     fRet = TRUE;
  588.     goto Ret;
  589. }
  590. if (!fAutoDial) //  if it's not set, go ahead and try the network
  591. {
  592.     if (fPing)
  593. fRet = SmartPing();
  594.     else 
  595. fRet = TRUE;
  596.     goto Ret;
  597. }
  598. else // See if we have an active RAS connection
  599. {
  600.     WRITE_LOG0("Any active RAS connection?");
  601.     DWORD cRasConns = 0;
  602.     RASCONN rgRasConn[5];
  603.     DWORD cb = sizeof(rgRasConn);
  604.     
  605.     if (!m_hRasApiModule)
  606.     m_hRasApiModule= LoadLibrary(OS_STRING("rasapi32.dll"));
  607.     // add this code if this ever gets into win16; win16 will return nonnull on error from load library  #if _WIN16 if (m_handle < HINSTANCE_ERROR) m_handle = NULL; #endif
  608.     if (!m_hRasApiModule) // Dialup networking is not installed.
  609.     {
  610.     WRITE_LOG0("Dialup networking not installed ...");
  611.          if (fPing)
  612.      fRet = SmartPing();
  613.          else 
  614.      fRet = TRUE;
  615.     goto Ret;
  616.     }
  617.     if (!m_pRasEnumConnections)
  618.     m_pRasEnumConnections =
  619. (FPRASENUMCONNECTIONS)GetProcAddress(m_hRasApiModule,OS_STRING("RasEnumConnectionsA"));
  620.     if (!m_pRasEnumConnections) // Dialup networking is not installed.
  621.     {
  622.     WRITE_LOG0("Cannot load RasEnumConnections fc...");
  623.     if (fPing)
  624.      fRet = SmartPing();
  625.          else 
  626.      fRet = TRUE;
  627.     goto Ret;
  628.     }
  629.     rgRasConn[0].dwSize = sizeof(RASCONN);
  630.     m_pRasEnumConnections(rgRasConn, &cb, &cRasConns);
  631.     
  632.     WRITE_LOG1("RasEnumConnections found %d connections...", cRasConns);
  633.     if (cRasConns)
  634.     {
  635.          if (fPing)
  636.      fRet = SmartPing();
  637.          else 
  638.      fRet = TRUE;
  639.     goto Ret;
  640.     }
  641.     else
  642.     {
  643.     fRet = FALSE;
  644.     goto Ret;
  645.     }
  646. }
  647. Ret:
  648. // free up the library if loaded
  649. if (m_hRasApiModule)
  650. {
  651.     ::FreeLibrary(m_hRasApiModule);
  652.     m_hRasApiModule = NULL;
  653. }
  654. #if !defined(WIN32_PLATFORM_PSPC)
  655. // reset the error mode to the old value
  656. ::SetErrorMode(oldErrorMode);
  657. #endif /* !defined(WIN32_PLATFORM_PSPC) */
  658. WRITE_LOG1("FInternetAvailable terminated with %s value...", 
  659.     fRet ? "TRUE" : "FALSE");
  660. return(fRet);
  661. #else
  662. // Win16 section
  663. // =-=w16.0  for win16 always returns true, since there is no popup dialog in the way
  664. return (TRUE);
  665. #endif
  666. }
  667. //******************************************************************************
  668. //
  669. // Method: CHXNetCheck::Ping
  670. //
  671. // Purpose: Tests to see if we can open a TCP connection to the given hostname. 
  672. // if fAynchronous is true, we call back a response object provided by
  673. // the caller (through Init).  Otherwise we block.
  674. //
  675. //
  676. // Notes: n/a
  677. //
  678. //******************************************************************************
  679. BOOL CHXNetCheck::Ping(const char *szHostName, UINT16 nPort, BOOL fAsynchronous)
  680. {
  681. ULONG32 ulStartTime, ulCurrentTime, ulElapsedTime;
  682. BOOL fRet = FALSE;
  683. // If we don't have the network services interface yet than try and get it here
  684. if (m_pContext && !m_pRmaNetServices)
  685.     m_pContext->QueryInterface(IID_IHXNetworkServices, (void**)&m_pRmaNetServices);
  686. if (!m_pRmaTCPSocket && m_pRmaNetServices)
  687. m_pRmaNetServices->CreateTCPSocket(&m_pRmaTCPSocket);
  688. if (!m_pRmaTCPSocket)
  689. return FALSE;
  690. m_fFailed = m_fConnected = FALSE;
  691. m_pRmaTCPSocket->Init(this);
  692. m_pRmaTCPSocket->Connect(szHostName, nPort);
  693. ulElapsedTime = 0;
  694. // Get start time
  695. ulStartTime = HX_GET_TICKCOUNT();
  696. while (!m_fFailed && !m_fConnected && (ulElapsedTime < m_Timeout))
  697. {
  698. SleepWell(1000);
  699.     ulCurrentTime = HX_GET_TICKCOUNT();
  700. ulElapsedTime = CALCULATE_ELAPSED_TICKS(ulStartTime, ulCurrentTime);
  701. }
  702. fRet = m_fConnected;
  703. m_pRmaTCPSocket->Release();
  704. m_pRmaTCPSocket = NULL;
  705. return (fRet);
  706. }
  707. //******************************************************************************
  708. //
  709. // Method: CHXNetCheck::SleepWell
  710. //
  711. // Purpose: This method sleeps but continues to pump messages.  This allows us to 
  712. // block properly, even under such platforms as Win16.
  713. //
  714. //
  715. // Notes: n/a
  716. //
  717. //******************************************************************************
  718. void CHXNetCheck::SleepWell(ULONG32 ulInterval)
  719. {
  720. ULONG32 ulStartTime, ulCurrentTime;
  721. MSG     msg;
  722. char szClassName[16] = ""; /* Flawfinder: ignore */
  723. HWND hActiveWindow = NULL;
  724. // Get start time
  725. ulStartTime = HX_GET_TICKCOUNT();
  726. do
  727. {
  728.     // Keep pumping messages
  729.         if(PeekMessage(&msg, NULL,0,0,PM_REMOVE))
  730.     {
  731. if(msg.message == WM_QUIT) 
  732. {
  733.     PostQuitMessage(0);
  734.     break;
  735. }
  736. else
  737. {
  738.     // XXXJDL Since we are pumping messages through here while we block elsewhere in the calling code
  739.     // we need to get the free dialog navigation support from windows for modeless dialog boxes.  Modal dialog
  740.     // boxes are fine since they will not go through this message loop.  I have been using this method
  741.     // in the main player application and it seems to work ok.  Let me know if this causes pain for anyone.
  742.     // Basically if the active window is a dialog box class then we are assuming it is a modeless dialog box
  743.     // and calling IsDialogMessage with this handle.  If the active window is neither a dialog class or
  744.     // doesn't process the dialog message then we do the normal message dispatching
  745.     hActiveWindow = ::GetActiveWindow();
  746.     szClassName[0] = '';
  747.     ::GetClassName(hActiveWindow,
  748.    OS_STRING2(szClassName,sizeof(szClassName)),
  749.    sizeof(szClassName));
  750.     if (strcmp(szClassName,DIALOG_CLASS) || !IsDialogMessage(hActiveWindow, &msg))
  751.     {
  752. TranslateMessage(&msg);
  753.         DispatchMessage(&msg);
  754.     }
  755. }
  756.     }
  757.     // If we have waited ulInterval time then drop out
  758.     ulCurrentTime = HX_GET_TICKCOUNT();
  759. } while (CALCULATE_ELAPSED_TICKS(ulStartTime, ulCurrentTime) < ulInterval);
  760. }
  761. //******************************************************************************
  762. //
  763. // Method: CHXNetCheck::GetDNSAddress
  764. //
  765. // Purpose: Determines the IP address of the user's primary DNS server.
  766. //
  767. // Notes: Returns IP address in numeric form, in a CHXString. An empty
  768. // string is returned when the IP address cannot be determined.
  769. //
  770. //******************************************************************************
  771. void CHXNetCheck::GetDNSAddress(CHXString& strDNS)
  772. {
  773. #ifndef _WIN16
  774.     strDNS.Empty();
  775.     HKEY hKey = 0;
  776.     // Win95/98 likes it here
  777.     RegOpenKey(HKEY_LOCAL_MACHINE, "System\CurrentControlSet\Services\VxD\MSTCP\Parameters",
  778.     &hKey);
  779.     if (hKey != NULL)
  780.     {
  781.      GetNameServerKey(hKey, "NameServer", strDNS);
  782. if (strDNS.IsEmpty())
  783.      GetNameServerKey(hKey, "DhcpNameServer", strDNS);
  784. RegCloseKey(hKey);
  785.     }
  786.     // Some Win 95/98 machines put it here..
  787.     if (strDNS.IsEmpty())
  788.     {
  789.      RegOpenKey(HKEY_LOCAL_MACHINE, OS_STRING("System\CurrentControlSet\Services\VxD\MSTCP"),
  790.      &hKey);
  791.     
  792.      if (hKey != NULL)
  793.      {
  794.          GetNameServerKey(hKey, "NameServer", strDNS);
  795. if (strDNS.IsEmpty())
  796.      GetNameServerKey(hKey, "DhcpNameServer", strDNS);
  797. RegCloseKey(hKey);
  798.      }
  799.     }
  800.     // Try WinNT registry location.
  801.     if (strDNS.IsEmpty())
  802.     {
  803. RegOpenKey(HKEY_LOCAL_MACHINE, OS_STRING("System\CurrentControlSet\Services\Tcpip\Parameters"),
  804. &hKey);
  805.     
  806.      if (hKey != NULL)
  807.      {
  808.          GetNameServerKey(hKey, "NameServer", strDNS);
  809. if (strDNS.IsEmpty())
  810.      GetNameServerKey(hKey, "DhcpNameServer", strDNS);
  811. RegCloseKey(hKey);
  812.      }
  813.     }
  814.     // Another  WinNT registry location.
  815.     if (strDNS.IsEmpty())
  816.     {
  817. RegOpenKey(HKEY_LOCAL_MACHINE, OS_STRING("System\CurrentControlSet\Services\Tcpip"),
  818. &hKey);
  819.      if (hKey != NULL)
  820.      {
  821.          GetNameServerKey(hKey, "NameServer", strDNS);
  822. if (strDNS.IsEmpty())
  823.      GetNameServerKey(hKey, "DhcpNameServer", strDNS);
  824. RegCloseKey(hKey);
  825.      }
  826.     }
  827.     // I want to make sure this technique of getting DNS address works. Email bpitzel
  828.     // if it fails.
  829.     HX_ASSERT( !strDNS.IsEmpty() );
  830. #endif
  831. }
  832. #ifndef _WIN16
  833. void CHXNetCheck::GetNameServerKey(HKEY hKey, const char* szKeyName, CHXString& strDNS)
  834. {
  835.     char szDNS[MAX_PATH]; /* Flawfinder: ignore */
  836.     DWORD regType = 0 ;
  837.     DWORD cbuf;
  838.     cbuf = sizeof(szDNS);
  839.     if (ERROR_SUCCESS == RegQueryValueEx(hKey, OS_STRING(szKeyName), NULL, &regType, (BYTE *) szDNS, &cbuf))
  840.     {
  841. strDNS = szDNS;
  842. // Win95/98 separate multiple DNS addresses with ","
  843. // WinNT separate multiple DNS addresses with ","
  844. // We will grab the first IP address in the list
  845. strDNS = strDNS.SpanExcluding(" ,");
  846. strDNS.TrimLeft();
  847. strDNS.TrimRight();
  848.     }
  849. }
  850. #endif /* _WIN16 */
  851. BOOL 
  852. CHXNetCheck::SmartPing()
  853. {
  854.     using namespace NetDetectLog; 
  855.     WRITE_LOG0("SmartPing fc entered ...");
  856.     UINT16 nPort = DEF_HTTP_PORT;
  857.     // try to get DNS address to ping.
  858.     CHXString strPingAddr;
  859.     GetDNSAddress(strPingAddr);
  860.     nPort = DEF_DNS_PORT;
  861.     
  862.     // No DNS address? Default to a known web server.
  863.     // XXXBJP pinging video.real.com, used in Beta 1, should be
  864.     // changed for Beta 2!!
  865.     if (strPingAddr.IsEmpty())
  866.     {
  867.     strPingAddr = "209.66.98.23"; // video.real.com .. UGHHHH!!
  868.     nPort = DEF_HTTP_PORT;
  869.     }
  870.     return (Ping(strPingAddr, nPort, FALSE));
  871. }
  872. BOOL 
  873. CHXNetCheck::FNetCardActive()
  874. {
  875. #if defined(_WIN16) || defined(WIN32_PLATFORM_PSPC)
  876.     return TRUE;
  877. #else
  878. char szEnum_Name[ENUM_MAX][20] = {REGSTR_KEY_ROOTENUM, REGSTR_KEY_BIOSENUM, /* Flawfinder: ignore */
  879. REGSTR_KEY_PCIENUM, REGSTR_KEY_ISAENUM, REGSTR_KEY_EISAENUM,
  880. REGSTR_KEY_PCMCIAENUM};
  881.     using namespace NetDetectLog;
  882.     WRITE_LOG0("FNetCardActive entered ...");
  883.     char szClass[64]; /* Flawfinder: ignore */
  884.     ULONG ulType;
  885.     ULONG32 cbData;
  886.     CHXString sCurrentKey;
  887.     CHXString sCurrentFiles;
  888.     CHXString sDeviceID;
  889.     char szCurrentDeviceNode[STRINGSIZE]; /* Flawfinder: ignore */
  890.     char szCurrentDevice[STRINGSIZE]; /* Flawfinder: ignore */
  891.     char szSoftwareKey[STRINGSIZE]; /* Flawfinder: ignore */
  892.     BOOL bFoundKey = FALSE;
  893.     int i;
  894.     OSVERSIONINFO osv;
  895.     memset(&osv, 0, sizeof(osv));
  896.     osv.dwOSVersionInfoSize = sizeof(osv);
  897.     GetVersionEx(&osv);
  898.     if (osv.dwPlatformId == VER_PLATFORM_WIN32_NT)
  899.      return FALSE;
  900.     using namespace NetDetectLog; 
  901.     WRITE_LOG0("Win95/98 : Looking for devices of the Net class...");
  902.     // we want to look through the ENUMBIOS, ENUMISAPNP, and ENUMPCI
  903.     // trees for devices of the Net class.  For each display device
  904.     // we find we will ask the Device manager if it is active.
  905.     for (i = 0; i < ENUM_MAX && !bFoundKey; i++)
  906.     {
  907. HKEY hBusKey = 0;
  908. sCurrentKey = (CHXString)REGSTR_KEY_ENUM + BACKSLASH + szEnum_Name[i];
  909. // Start reading the devices
  910. if (!RegOpenKeyEx(HKEY_LOCAL_MACHINE, OS_STRING(sCurrentKey), 0, KEY_READ, &hBusKey))
  911. {
  912.     cbData = sizeof(szCurrentDeviceNode);
  913.     ULONG32 dwDevNodeEnumIndex = 0;
  914.     HKEY hDeviceNodeKey;
  915.     // Enumerate all of the devices on the bus
  916.     while (!bFoundKey &&
  917. !RegEnumKeyEx(hBusKey, dwDevNodeEnumIndex, 
  918.       OS_STRING2(szCurrentDeviceNode, cbData), 
  919.       &cbData, NULL, NULL, NULL, NULL))
  920.     {
  921. // get the registry key for the current device node
  922. hDeviceNodeKey = 0;
  923. sCurrentKey = (CHXString)REGSTR_KEY_ENUM + BACKSLASH + szEnum_Name[i] + BACKSLASH + (CHXString)szCurrentDeviceNode;
  924. if (!RegOpenKeyEx(HKEY_LOCAL_MACHINE, OS_STRING(sCurrentKey), 0, KEY_READ, &hDeviceNodeKey))
  925. {
  926.     cbData = sizeof(szCurrentDevice);
  927.     ULONG32 dwHWDeviceEnumIndex = 0;
  928.     HKEY hHWDeviceKey;
  929.     // enumerate all of the hardware devices in this Device Node
  930.     while (!bFoundKey &&
  931. !RegEnumKeyEx(hDeviceNodeKey, dwHWDeviceEnumIndex, 
  932.       OS_STRING2(szCurrentDevice, cbData),
  933.       &cbData, NULL, NULL, NULL, NULL))
  934.     {
  935. // get the registry key for the current hardware device
  936. int nBaseKeyLength = sCurrentKey.GetLength();
  937. hHWDeviceKey = 0;
  938. sCurrentKey += BACKSLASH + (CHXString) szCurrentDevice;
  939. if (!RegOpenKeyEx(HKEY_LOCAL_MACHINE, OS_STRING(sCurrentKey), 0, KEY_READ, &hHWDeviceKey))
  940. {
  941.     // Ask if this device is a "Net" device by checking it's class
  942.     cbData = sizeof(szClass);
  943.     szClass[0] = 0;
  944.     if (!RegQueryValueEx(hHWDeviceKey, OS_STRING("Class"), 0, &ulType, (LPBYTE) szClass, &cbData)
  945. && !stricmp(szClass, "Net"))
  946.     {
  947. // if it is a display device then we want to find out if it is
  948. // the active device for this class.
  949. cbData = sizeof(szSoftwareKey);
  950. sDeviceID = (CHXString) szEnum_Name[i] + BACKSLASH + (CHXString)szCurrentDeviceNode
  951.     + BACKSLASH + (CHXString) szCurrentDevice;
  952. // get the registry key name for the software key for the device, if it is not
  953. // null and this is the active device then we are done.
  954. if (!RegQueryValueEx(hHWDeviceKey, OS_STRING("Driver"), 0, &ulType, (LPBYTE) szSoftwareKey, &cbData)
  955.     && szSoftwareKey[0] != '0'
  956.     && DevNodeIsActive((sDeviceID))) // Call Config Manager in 16-bit code
  957. {
  958.     bFoundKey = TRUE;
  959.     WRITE_LOG1("Found active node %s...", (char const*) sDeviceID);
  960. }
  961.     }
  962. }
  963. // reset the key to the base for the next time through the loop
  964. sCurrentKey = sCurrentKey.Left(nBaseKeyLength);
  965. if (hHWDeviceKey)
  966. {
  967.     RegCloseKey(hHWDeviceKey);
  968.     hHWDeviceKey = 0;
  969. }
  970. // move to next device key
  971. dwHWDeviceEnumIndex++;
  972. // reset size to szCurrentDevice size
  973. cbData = sizeof(szCurrentDevice);
  974.     }
  975. }
  976. if (hDeviceNodeKey)
  977. {
  978.     RegCloseKey(hDeviceNodeKey);
  979.     hDeviceNodeKey = 0;
  980. }
  981. // move to the next device node
  982. dwDevNodeEnumIndex++;
  983. // reset the size to szCurrentDeviceNode size
  984. cbData = sizeof(szCurrentDeviceNode);
  985.     }
  986. }
  987. if (hBusKey)
  988. {
  989.     RegCloseKey(hBusKey);
  990.     hBusKey = 0;
  991. }
  992.     }
  993.     return bFoundKey;
  994. #endif
  995. }
  996. BOOL
  997. CHXNetCheck::DevNodeIsActive(const char *szDeviceID)
  998. {
  999. #ifdef _WIN16
  1000.     HX_ASSERT (FALSE); // should never get called in win16
  1001.     return TRUE;
  1002. #else
  1003.     HINSTANCE hDevNodeInst;
  1004.     DWORD dwStatus;
  1005.     DWORD dwProblemNumber;
  1006.     DWORD cr;
  1007.     DWORD (WINAPI * pGetDevNodeStatus32Call) (const char *, LPDWORD, LPDWORD);
  1008.     // 10 is a magic number for the configuration manager api
  1009.     // that eventually gets called in the 16 bit dll.
  1010.     dwStatus = dwProblemNumber = cr = 10;
  1011.     pGetDevNodeStatus32Call = 0;
  1012.     hDevNodeInst = LoadLibrary(OS_STRING(_32BIT_DLLNAME));
  1013.     // add this code if this ever gets into win16; win16 will return nonnull on error from load library  #if _WIN16 if (m_handle < HINSTANCE_ERROR) m_handle = NULL; #endif
  1014.     if (hDevNodeInst)
  1015.     {
  1016. (FARPROC &) pGetDevNodeStatus32Call = GetProcAddress(hDevNodeInst, OS_STRING("GetDevNodeStatus32Call"));
  1017. if (pGetDevNodeStatus32Call)
  1018. {
  1019.     cr = pGetDevNodeStatus32Call(szDeviceID, &dwStatus, &dwProblemNumber);
  1020. }
  1021. FreeLibrary(hDevNodeInst);
  1022.     }
  1023.     if (cr == 0
  1024. && dwProblemNumber == 0)
  1025. return TRUE;
  1026.     else
  1027. return FALSE;
  1028. #endif
  1029. }
  1030. HX_RESULT CHXNetCheck::WinInetGetConnected(BOOL& bConnected)
  1031. {
  1032.     using namespace NetDetectLog; 
  1033.     WRITE_LOG0("WinInetGetConnected fc entered...");
  1034.     HINSTANCE hWinInet;
  1035.     BOOL (WINAPI * pInternetGetConnectedState) (LPDWORD lpdwFlags, DWORD dwReserved);
  1036.     hWinInet= LoadLibrary(OS_STRING(WININET_DLL));
  1037.     if (!hWinInet)
  1038.     {
  1039. WRITE_LOG0("WinInet not present ...");
  1040.      return HXR_FAIL;
  1041.     }
  1042.     (FARPROC &) pInternetGetConnectedState = GetProcAddress(hWinInet, OS_STRING("InternetGetConnectedState"));
  1043.     if (!pInternetGetConnectedState )
  1044.     {
  1045. WRITE_LOG0("InternetGetConnectedState fc cannot be loaded ...");
  1046.     
  1047. FreeLibrary(hWinInet);
  1048.      return HXR_FAIL;
  1049.     }
  1050.     DWORD dwFlags;
  1051.     bConnected = pInternetGetConnectedState(&dwFlags, 0);
  1052.     FreeLibrary(hWinInet);
  1053.     
  1054.     WRITE_LOG_WINET(bConnected, dwFlags);
  1055. #ifdef _INTERNETGETCONNECTEDSTATE_MAY_FAIL_UNEXPECTEDELY_
  1056.     if (!bConnected) return HXR_FAIL;
  1057. #else  //_INTERNETGETCONNECTEDSTATE_MAY_FAIL_UNEXPECTEDELY_
  1058.     return HXR_OK;
  1059. #endif //_INTERNETGETCONNECTEDSTATE_MAY_FAIL_UNEXPECTEDELY_
  1060. }