Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

714 lines
17 KiB

  1. //
  2. // Copyright (C) 2001 Microsoft Corp
  3. //
  4. // MyAlg.cpp : Implementation of DLL Exports.
  5. //
  6. // Sanjiv
  7. // JPDup
  8. //
  9. #include "PreComp.h"
  10. #include "MyAlg.h"
  11. CControlObjectList g_ControlObjectList;
  12. IApplicationGatewayServices* g_pIAlgServicesAlgFTP = NULL;
  13. USHORT g_nFtpPort=0;
  14. HANDLE g_hNoMorePendingConnection=NULL;
  15. bool g_bStoping = false;
  16. //
  17. // got to move WSAStartup to Initialize
  18. //
  19. CAlgFTP::CAlgFTP ()
  20. {
  21. MYTRACE_ENTER("CAlgFTP::CAlgFTP ");
  22. m_ListenAddress = 0;
  23. m_ListenPort = 0;
  24. m_ListenSocket = INVALID_SOCKET;
  25. m_pPrimaryControlChannel = NULL;
  26. m_hNoMoreAccept = NULL;
  27. g_bStoping = false;
  28. WSADATA wsaData;
  29. DWORD Err;
  30. Err = WSAStartup(MAKEWORD(2,2),&wsaData);
  31. _ASSERT(Err == 0);
  32. MyHelperInitializeBufferManagement();
  33. }
  34. //
  35. // Destructor
  36. //
  37. CAlgFTP ::~CAlgFTP ()
  38. {
  39. MYTRACE_ENTER("CAlgFTP::~CAlgFTP ");
  40. if ( g_pIAlgServicesAlgFTP )
  41. {
  42. MYTRACE("Releasing AlgServices");
  43. g_pIAlgServicesAlgFTP->Release();
  44. g_pIAlgServicesAlgFTP = NULL;
  45. }
  46. if ( g_hNoMorePendingConnection )
  47. {
  48. CloseHandle(g_hNoMorePendingConnection);
  49. g_hNoMorePendingConnection = NULL;
  50. }
  51. if ( m_hNoMoreAccept )
  52. {
  53. CloseHandle(m_hNoMoreAccept);
  54. m_hNoMoreAccept = NULL;
  55. }
  56. WSACleanup();
  57. MyHelperShutdownBufferManagement();
  58. }
  59. //
  60. // Initialize can be called in two cases
  61. // 1. From the main IApplicationGateway::Initialize
  62. // 2. From AcceptCompletionRoutine when some FatalSocket Error has occured, which forced the
  63. // closure of the m_ListenSocket and all the control connections/data connections etc.
  64. // (This call to ShutDown will terminate all current ControlSessions. Might not be necessary.
  65. // But if AcceptCompletion returned error we do it anyway.)
  66. //
  67. STDMETHODIMP
  68. CAlgFTP ::Initialize(
  69. IApplicationGatewayServices * pAlgServices
  70. )
  71. {
  72. MYTRACE_ENTER("CAlgFTP::Initialize");
  73. pAlgServices->AddRef();
  74. g_pIAlgServicesAlgFTP = pAlgServices;
  75. if ( FAILED(GetFtpPortToUse(g_nFtpPort)) )
  76. g_nFtpPort = 21; // Use the standard ftp port 21
  77. MYTRACE("USING FTP PORT %d", g_nFtpPort);
  78. HRESULT hr = RedirectToMyPort();
  79. if ( FAILED(hr) )
  80. CleanUp();
  81. return hr;
  82. }
  83. //
  84. // ALG.exe will call this interface to terminat
  85. // this ALG FTP PlugIn
  86. //
  87. STDMETHODIMP
  88. CAlgFTP::Stop()
  89. {
  90. MYTRACE_ENTER("CAlgFTP::Stop");
  91. CleanUp();
  92. return S_OK;
  93. }
  94. #define REG_KEY_ALG_FTP TEXT("SOFTWARE\\Microsoft\\ALG\\ISV\\{6E590D61-F6BC-4dad-AC21-7DC40D304059}")
  95. #define REG_VALUE_FTP_PORT TEXT("UsePort")
  96. HRESULT
  97. CAlgFTP::GetFtpPortToUse(
  98. USHORT& usPort
  99. )
  100. {
  101. MYTRACE_ENTER("CAlgFTP:GetFtpPortToUse");
  102. DWORD dwPort = 0;
  103. //
  104. // Did you modify the default FTP Port
  105. //
  106. LONG lRet;
  107. CRegKey RegKeyAlgFTP;
  108. MYTRACE("Looking in RegKey \"%S\"", REG_KEY_ALG_FTP);
  109. lRet = RegKeyAlgFTP.Open(HKEY_LOCAL_MACHINE, REG_KEY_ALG_FTP, KEY_READ);
  110. if ( ERROR_SUCCESS == lRet )
  111. {
  112. LONG lRet = RegKeyAlgFTP.QueryValue(dwPort, REG_VALUE_FTP_PORT);
  113. if ( ERROR_SUCCESS == lRet )
  114. {
  115. MYTRACE("Found the \"%S\" value %d", REG_VALUE_FTP_PORT, dwPort);
  116. usPort = (USHORT) dwPort;
  117. }
  118. else
  119. {
  120. MYTRACE("\"%S\" Value not set", REG_VALUE_FTP_PORT);
  121. return E_FAIL;
  122. }
  123. }
  124. else
  125. {
  126. MYTRACE("Could not open regkey", lRet);
  127. return E_FAIL;
  128. }
  129. return S_OK;
  130. }
  131. extern CComAutoCriticalSection m_AutoCS_FtpIO; // See FtpControl.cpp
  132. //
  133. //
  134. //
  135. void
  136. CAlgFTP::CleanUp()
  137. {
  138. MYTRACE_ENTER("CAlgFTP::CleanUp()");
  139. g_bStoping = true;
  140. //
  141. // Free socket
  142. //
  143. if ( INVALID_SOCKET != m_ListenSocket )
  144. {
  145. MYTRACE("CAlgFTP::STOP ACCEPTING NEW CONNECTION !!");
  146. m_AutoCS_FtpIO.Lock();
  147. m_hNoMoreAccept = CreateEvent(NULL, false, false, NULL);
  148. closesocket(m_ListenSocket);
  149. m_ListenSocket = INVALID_SOCKET;
  150. m_AutoCS_FtpIO.Unlock();
  151. if ( m_hNoMoreAccept )
  152. {
  153. WaitForSingleObject(
  154. m_hNoMoreAccept,
  155. INFINITE
  156. );
  157. }
  158. }
  159. if ( m_pPrimaryControlChannel )
  160. {
  161. MYTRACE("Cancelling PrimaryControl");
  162. HRESULT hr = m_pPrimaryControlChannel->Cancel();
  163. MYTRACE("Releasing Primary");
  164. m_pPrimaryControlChannel->Release();
  165. m_pPrimaryControlChannel = NULL;
  166. }
  167. m_AutoCS_FtpIO.Lock();
  168. if ( g_ControlObjectList.m_NumElements == 0 )
  169. {
  170. MYTRACE("List for FTPconnections is empty");
  171. m_AutoCS_FtpIO.Unlock();
  172. }
  173. else
  174. {
  175. //
  176. // Pending connection are still active
  177. // shut them down a wait till the last one is free
  178. //
  179. MYTRACE("Empty the list of FTPconnections (%d)", g_ControlObjectList.m_NumElements);
  180. g_hNoMorePendingConnection = CreateEvent(NULL, false, false, NULL);
  181. MYTRACE("Closing all connections");
  182. g_ControlObjectList.ShutdownAll();
  183. m_AutoCS_FtpIO.Unlock();
  184. MYTRACE("Waiting for last connection to notify us");
  185. WaitForSingleObject(
  186. g_hNoMorePendingConnection,
  187. 2000 // Will give them 2 second max to close vs using INFINITE
  188. );
  189. MYTRACE("Got signal no more pending connection");
  190. }
  191. }
  192. /*
  193. We have this private function to get the OriginalDestionationInfo
  194. and to get the type of connection it is. Whether it is INCOMING or OUTGOING.
  195. */
  196. HRESULT
  197. CAlgFTP::MyGetOriginalDestinationInfo(
  198. PUCHAR AcceptBuffer,
  199. ULONG* pAddr,
  200. USHORT* pPort,
  201. CONNECTION_TYPE* pConnType
  202. )
  203. {
  204. MYTRACE_ENTER("CAlgFTP::MyGetOriginalDestinationInfo");
  205. IAdapterInfo *pAdapterInfo = NULL;
  206. HRESULT hr = S_OK;
  207. ULONG RemoteAddr = 0;
  208. USHORT RemotePort = 0;
  209. ALG_ADAPTER_TYPE Type;
  210. MyHelperQueryAcceptEndpoints(
  211. AcceptBuffer,
  212. 0,
  213. 0,
  214. &RemoteAddr,
  215. &RemotePort
  216. );
  217. MYTRACE("Source Address %s:%d", MYTRACE_IP(RemoteAddr), ntohs(RemotePort));
  218. hr = m_pPrimaryControlChannel->GetOriginalDestinationInformation(
  219. RemoteAddr,
  220. RemotePort,
  221. pAddr,
  222. pPort,
  223. &pAdapterInfo
  224. );
  225. if ( SUCCEEDED(hr) )
  226. {
  227. hr = pAdapterInfo->GetAdapterType(&Type);
  228. if (SUCCEEDED(hr) )
  229. {
  230. ULONG ulAddressCount;
  231. ULONG* arAddresses;
  232. hr = pAdapterInfo->GetAdapterAddresses(&ulAddressCount, &arAddresses);
  233. if ( SUCCEEDED(hr) )
  234. {
  235. if ( ulAddressCount > 0 )
  236. {
  237. bool bFromIcsBox = FALSE;
  238. while (ulAddressCount && !bFromIcsBox) {
  239. if (arAddresses[--ulAddressCount] == RemoteAddr)
  240. bFromIcsBox = TRUE;
  241. }
  242. MYTRACE("Address count %d address[0] %s", ulAddressCount, MYTRACE_IP(arAddresses[0]));
  243. switch (Type)
  244. {
  245. case eALG_PRIVATE:
  246. MYTRACE("Adapter is Private");
  247. if ( bFromIcsBox )
  248. {
  249. *pConnType = INCOMING;
  250. MYTRACE("InComing");
  251. }
  252. else
  253. {
  254. *pConnType = OUTGOING;
  255. MYTRACE("OutGoing");
  256. }
  257. break;
  258. case eALG_BOUNDARY:
  259. case eALG_FIREWALLED:
  260. case eALG_BOUNDARY|eALG_FIREWALLED:
  261. MYTRACE("Adapter is Public or/and Firewalled");
  262. if ( bFromIcsBox )
  263. {
  264. *pConnType = OUTGOING;
  265. MYTRACE("OutGoing");
  266. }
  267. else
  268. {
  269. *pConnType = INCOMING;
  270. MYTRACE("InComing");
  271. }
  272. break;
  273. default:
  274. MYTRACE("Adapter is ????");
  275. _ASSERT(FALSE);
  276. hr = E_FAIL;
  277. break;
  278. }
  279. }
  280. }
  281. CoTaskMemFree(arAddresses);
  282. }
  283. pAdapterInfo->Release();
  284. }
  285. else
  286. {
  287. MYTRACE_ERROR("from GetOriginalDestinationInformation", hr);
  288. }
  289. return hr;
  290. }
  291. /*
  292. Can be called in 2 cases.
  293. 1. AcceptEx has actually succeeded or failed
  294. If Succeeded we make a new CFtpControlConnection giving it the AcceptedSocket
  295. And reissue the Accept
  296. If Failed and not fatal failure we just reissue the Accept
  297. If Failed and Fatal Failure we ShutDown gracefully. Restart the a new listen
  298. 2. Because we closed the listening socket in STOP => ErrCode = ERROR_IO_CANCELLED
  299. in which case we just return
  300. */
  301. void
  302. CAlgFTP::AcceptCompletionRoutine(
  303. ULONG ErrCode,
  304. ULONG BytesTransferred,
  305. PNH_BUFFER Bufferp
  306. )
  307. {
  308. MYTRACE_ENTER("CAlgFTP::AcceptCompletionRoutine");
  309. #if defined(DBG) || defined(_DEBUG)
  310. if ( 0 != ErrCode )
  311. {
  312. MYTRACE("ErrCode : %x", ErrCode);
  313. MYTRACE("MyHelperIsFatalSocketError(ErrCode) is %d", MyHelperIsFatalSocketError(ErrCode));
  314. }
  315. #endif
  316. ULONG OriginalAddress = 0;
  317. USHORT OriginalPort = 0;
  318. CONNECTION_TYPE ConnType;
  319. HRESULT hr;
  320. ULONG Err;
  321. if ( ERROR_IO_CANCELLED == ErrCode || g_bStoping )
  322. {
  323. MYTRACE("CAlgFTP::AcceptCompletionRoutine-ERROR_IO_CANCELLED");
  324. //
  325. // Ok we are closing here MyAlg->Stop got called
  326. // no need to attemp a new Listen/Accept incoming
  327. //
  328. MYTRACE("------NORMAL TERMINATION (not creating a new listen/accept)-----");
  329. MyHelperReleaseBuffer(Bufferp);
  330. if ( m_hNoMoreAccept )
  331. SetEvent(m_hNoMoreAccept);
  332. return; // Normal termination
  333. }
  334. SOCKET AcceptedSocket = Bufferp->Socket;
  335. if ( ErrCode && MyHelperIsFatalSocketError(ErrCode) )
  336. {
  337. MYTRACE_ERROR("CAlgFTP::AcceptCompletionRoutine-FATAL ERROR", ErrCode);
  338. //
  339. // Socket Routines says that we have a problem
  340. // so clean up and try a new redirection
  341. //
  342. if ( AcceptedSocket != INVALID_SOCKET )
  343. {
  344. MYTRACE("CLOSING ACCEPTED SOCKET!!");
  345. closesocket(AcceptedSocket);
  346. }
  347. hr = RedirectToMyPort();
  348. MyHelperReleaseBuffer(Bufferp);
  349. return;
  350. }
  351. if ( 0 == ErrCode )
  352. {
  353. //
  354. // Everything is good lets accept the connection
  355. //
  356. hr = MyGetOriginalDestinationInfo(Bufferp->Buffer,&OriginalAddress,&OriginalPort,&ConnType);
  357. if ( SUCCEEDED(hr) )
  358. {
  359. Err = setsockopt(
  360. AcceptedSocket,
  361. SOL_SOCKET,
  362. SO_UPDATE_ACCEPT_CONTEXT,
  363. (char *)&m_ListenSocket,
  364. sizeof(m_ListenSocket)
  365. );
  366. MYTRACE("setsockopt SO_UPDATE_ACCEPT_CONTEXT %x", Err);
  367. CFtpControlConnection *pFtpControlConnection = new CFtpControlConnection;
  368. if ( pFtpControlConnection )
  369. {
  370. hr = pFtpControlConnection->Init(
  371. AcceptedSocket,
  372. OriginalAddress,
  373. OriginalPort,
  374. ConnType
  375. );
  376. if ( SUCCEEDED(hr) )
  377. {
  378. g_ControlObjectList.Insert(pFtpControlConnection);
  379. }
  380. else
  381. {
  382. MYTRACE_ERROR("pFtpControlConnection->Init failed", hr);
  383. // No need to close at this time the closesocket(AcceptedSocket);
  384. // when the Init fails it will deref the newly created CFtpControlConnection
  385. // and will hit ZERO ref count and close the socket
  386. }
  387. }
  388. else
  389. {
  390. MYTRACE_ERROR("memory low, new pFtpControlConnection failed - CLOSING ACCEPTED SOCKET!!", 0);
  391. if ( AcceptedSocket != INVALID_SOCKET )
  392. closesocket(AcceptedSocket);
  393. }
  394. }
  395. else
  396. {
  397. MYTRACE_ERROR("MyGetOriginalDestinationInfo failed - CLOSING ACCEPTED SOCKET!!", hr);
  398. if ( AcceptedSocket != INVALID_SOCKET )
  399. closesocket(AcceptedSocket);
  400. }
  401. AcceptedSocket = INVALID_SOCKET;
  402. }
  403. Err = MyHelperAcceptStreamSocket(
  404. NULL,
  405. m_ListenSocket,
  406. AcceptedSocket,
  407. Bufferp,
  408. MyAcceptCompletion,
  409. (void *)this,
  410. NULL
  411. );
  412. if ( Err )
  413. {
  414. MYTRACE_ERROR("From MyHelperAcceptStreamSocket", Err);
  415. if ( AcceptedSocket != INVALID_SOCKET )
  416. {
  417. MYTRACE("CLOSING ACCEPTED SOCKET!!");
  418. closesocket(AcceptedSocket);
  419. AcceptedSocket = INVALID_SOCKET;
  420. }
  421. RedirectToMyPort();
  422. MyHelperReleaseBuffer(Bufferp);
  423. }
  424. return;
  425. }
  426. //
  427. // called From InitCAlgFTP
  428. // Will just create a socket bound to LOOP BACK adapter.
  429. //
  430. ULONG
  431. CAlgFTP::MakeListenerSocket()
  432. {
  433. MYTRACE_ENTER("CAlgFTP::MakeListenerSocket");
  434. if ( INVALID_SOCKET != m_ListenSocket )
  435. {
  436. //
  437. // Since this function is call on the starting point (See Initialize)
  438. // and also when a Accept error occured and needs a new redirect
  439. // we may already have a Socket created so let's free it
  440. //
  441. MYTRACE ("Remove current ListenSocket");
  442. closesocket(m_ListenSocket);
  443. m_ListenSocket = INVALID_SOCKET;
  444. }
  445. ULONG Err;
  446. ULONG Addr = inet_addr("127.0.0.1");
  447. Err = MyHelperCreateStreamSocket(Addr,0,&m_ListenSocket);
  448. if ( ERROR_SUCCESS == Err )
  449. {
  450. Err = MyHelperQueryLocalEndpointSocket(m_ListenSocket,&m_ListenAddress,&m_ListenPort);
  451. MYTRACE ("Listen on %s:%d", MYTRACE_IP(m_ListenAddress), ntohs(m_ListenPort));
  452. }
  453. else
  454. {
  455. MYTRACE_ERROR("MyHelperCreateStreamSocket", Err);
  456. }
  457. _ASSERT(Err == 0);
  458. return Err;
  459. }
  460. //
  461. // Redirect trafic destinated for PORT FTP_CONTROL_PORT(21)
  462. // to our listening socket (127.0.0.1) port (Allocated by MakeListenerSocket())
  463. //
  464. ULONG
  465. CAlgFTP::RedirectToMyPort()
  466. {
  467. MYTRACE_ENTER("CAlgFTP::RedirectToMyPort()");
  468. if ( ERROR_SUCCESS == MakeListenerSocket() )
  469. {
  470. if ( m_pPrimaryControlChannel )
  471. {
  472. //
  473. // Since this function is call on the starting point (See Initialize)
  474. // and also when a Accept error occured and needs a new redirect
  475. // we may already have a PrimaryControlChannel created so let's free it
  476. //
  477. MYTRACE("Releasing PrimaryControl");
  478. m_pPrimaryControlChannel->Cancel();
  479. m_pPrimaryControlChannel->Release();
  480. m_pPrimaryControlChannel = NULL;
  481. }
  482. //
  483. // ask for a redirection
  484. //
  485. HRESULT hr = g_pIAlgServicesAlgFTP->CreatePrimaryControlChannel(
  486. eALG_TCP,
  487. htons(g_nFtpPort), // 21 is the most common one
  488. eALG_DESTINATION_CAPTURE,
  489. TRUE,
  490. m_ListenAddress,
  491. m_ListenPort,
  492. &m_pPrimaryControlChannel
  493. );
  494. if ( SUCCEEDED(hr) )
  495. {
  496. //
  497. // Start listening
  498. //
  499. int nRetCode = listen( m_ListenSocket, 5);
  500. if ( SOCKET_ERROR != nRetCode )
  501. {
  502. ULONG Err = MyHelperAcceptStreamSocket(
  503. NULL,
  504. m_ListenSocket,
  505. INVALID_SOCKET,
  506. NULL,
  507. MyAcceptCompletion,
  508. (void *)this,NULL
  509. );
  510. if ( ERROR_SUCCESS == Err )
  511. {
  512. return S_OK;
  513. }
  514. else
  515. {
  516. MYTRACE_ERROR("FAILED TO START ACCEPT on 127.0.0.1:", Err);
  517. }
  518. }
  519. else
  520. {
  521. MYTRACE_ERROR("listen() failed ", nRetCode);
  522. }
  523. }
  524. else
  525. {
  526. MYTRACE_ERROR("from CreatePrimaryControlChannel", hr);
  527. }
  528. }
  529. //
  530. // if we got here that mean that one of the step above faild
  531. //
  532. MYTRACE_ERROR("Failed to RedirectToPort",E_FAIL)
  533. CleanUp();
  534. return E_FAIL;
  535. }