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.

745 lines
17 KiB

  1. /**********************************************************************/
  2. /** Microsoft Windows NT **/
  3. /** Copyright(c) Microsoft Corp., 1994 **/
  4. /**********************************************************************/
  5. /*
  6. connect.cxx
  7. This module contains the connection accept routine called by the connection
  8. thread.
  9. FILE HISTORY:
  10. Johnl 08-Aug-1994 Lifted from FTP server
  11. */
  12. #define INCL_INETSRV_INCS
  13. #include "smtpinc.h"
  14. #include <rdns.hxx>
  15. #include "smtpcli.hxx"
  16. //
  17. // Private prototypes.
  18. //
  19. VOID
  20. SmtpCompletion(
  21. PVOID pvContext,
  22. DWORD cbWritten,
  23. DWORD dwCompletionStatus,
  24. OVERLAPPED * lpo
  25. );
  26. SOCKERR CloseSocket( SOCKET sock );
  27. BOOL
  28. SendError(
  29. SOCKET socket,
  30. DWORD ids
  31. );
  32. //
  33. // Private functions.
  34. //
  35. /*++
  36. This function dereferences User data and kills the UserData object if the
  37. reference count hits 0. Before killing the user data, it also removes
  38. the connection from the list of active connections.
  39. --*/
  40. VOID DereferenceUserDataAndKill(IN OUT CLIENT_CONNECTION * pUserData)
  41. {
  42. pUserData->DisconnectClient();
  43. ((SMTP_CONNECTION*)pUserData)->QuerySmtpInstance()->RemoveConnection(pUserData);
  44. delete pUserData;
  45. } // DereferenceUserDataAndKill()
  46. void HandleErrorCondition(SOCKET sock, SMTP_SERVER_INSTANCE *pInstance, DWORD dwError, PSOCKADDR_IN pSockAddr)
  47. {
  48. char ErrorBuf[4 *MAX_PATH];
  49. const CHAR * apszSubStrings[2];
  50. CHAR pchAddr1[50] = "";
  51. CHAR pchAddr2[50] = "";
  52. DWORD SendSize;
  53. _ASSERT(pSockAddr != NULL);
  54. _ASSERT(pInstance != NULL);
  55. _ASSERT(sock != INVALID_SOCKET);
  56. SendSize = wsprintf (ErrorBuf, "%d-%s %s\r\n",
  57. SMTP_RESP_SRV_UNAVAIL,
  58. pInstance->GetConnectResponse(),
  59. SMTP_SRV_UNAVAIL_MSG);
  60. CLIENT_CONNECTION::WriteSocket(sock, ErrorBuf, SendSize );
  61. if(dwError == ERROR_REMOTE_SESSION_LIMIT_EXCEEDED)
  62. {
  63. _itoa(pInstance->QueryInstanceId(), pchAddr1, 10);
  64. InetNtoa(pSockAddr->sin_addr, pchAddr2 );
  65. apszSubStrings[0] = pchAddr1;
  66. apszSubStrings[1] = pchAddr2;
  67. SmtpLogEvent(SMTP_MAX_CONNECTION_REACHED,
  68. 2,
  69. apszSubStrings,
  70. NO_ERROR );
  71. }
  72. }
  73. BOOL VerifyClient(SMTP_CONNECTION * pNewClient, PSOCKADDR_IN psockAddrRemote)
  74. {
  75. AC_RESULT acIpAccess;
  76. BOOL fNeedDnsCheck = FALSE;
  77. BOOL fRet = TRUE;
  78. struct hostent* pH = NULL;
  79. pNewClient->QueryAccessCheck()->BindAddr( (PSOCKADDR)psockAddrRemote );
  80. if ( pNewClient->BindInstanceAccessCheck() )
  81. {
  82. acIpAccess = pNewClient->QueryAccessCheck()->CheckIpAccess( &fNeedDnsCheck);
  83. if ( (acIpAccess == AC_IN_DENY_LIST) ||
  84. ((acIpAccess == AC_NOT_IN_GRANT_LIST) && !fNeedDnsCheck) )
  85. {
  86. fRet = FALSE;
  87. }
  88. else if (fNeedDnsCheck)
  89. {
  90. pH = gethostbyaddr( (char*)(&((PSOCKADDR_IN)psockAddrRemote)->sin_addr),
  91. 4, PF_INET );
  92. if(pH != NULL)
  93. {
  94. acIpAccess = pNewClient->QueryAccessCheck()->CheckName(pH->h_name);
  95. }
  96. else
  97. {
  98. acIpAccess = AC_IN_DENY_LIST;
  99. }
  100. }
  101. if ( (acIpAccess == AC_IN_DENY_LIST) ||
  102. (acIpAccess == AC_NOT_IN_GRANT_LIST))
  103. {
  104. fRet = FALSE;
  105. }
  106. pNewClient->UnbindInstanceAccessCheck();
  107. }
  108. if(!fRet)
  109. {
  110. SetLastError(ERROR_ACCESS_DENIED);
  111. }
  112. return fRet;
  113. }
  114. BOOL
  115. ProcessNewClient(
  116. IN SOCKET sNew,
  117. IN PVOID EndpointObject,
  118. IN SMTP_SERVER_INSTANCE *pInstance,
  119. IN BOOL fMaxConnExceeded,
  120. IN PSOCKADDR_IN psockAddrRemote,
  121. IN PSOCKADDR_IN psockAddrLocal = NULL,
  122. IN PATQ_CONTEXT patqContext = NULL,
  123. IN PVOID pvBuff = NULL,
  124. IN DWORD cbWritten = 0,
  125. OUT LPBOOL pfAtqToBeFreed = NULL
  126. )
  127. {
  128. SMTP_CONNECTION * pNewClient = NULL;
  129. DWORD err = NO_ERROR;
  130. BOOL fReturn = FALSE;
  131. BOOL fMaxExceeded = FALSE;
  132. DBG_CODE( CHAR pchAddr[32];);
  133. BOOL fSockToBeFreed = TRUE;
  134. BOOL fDereferenceInstance = FALSE;
  135. CLIENT_CONN_PARAMS clientParams;
  136. TraceFunctEnterEx((LPARAM) NULL, "ProcessNewClient");
  137. DBG_CODE( InetNtoa( psockAddrRemote->sin_addr, pchAddr));
  138. if ( pfAtqToBeFreed != NULL)
  139. {
  140. *pfAtqToBeFreed = TRUE;
  141. }
  142. clientParams.sClient = sNew;
  143. clientParams.pAtqContext = patqContext;
  144. clientParams.pAddrLocal = (PSOCKADDR) psockAddrLocal;
  145. clientParams.pAddrRemote = (PSOCKADDR)psockAddrRemote;
  146. clientParams.pvInitialBuff = pvBuff;
  147. clientParams.cbInitialBuff = cbWritten ;
  148. clientParams.pEndpoint = (PIIS_ENDPOINT)EndpointObject;
  149. if( pInstance && (pInstance->IsShuttingDown() || (pInstance->QueryServerState( ) != MD_SERVER_STATE_STARTED)))
  150. {
  151. DBGPRINTF((DBG_CONTEXT," Service instance is shutting down\n"));
  152. }
  153. else if ( !fMaxConnExceeded)
  154. {
  155. // DBGPRINTF((DBG_CONTEXT,"Getting a connection object\n"));
  156. pNewClient = (SMTP_CONNECTION *) pInstance->CreateNewConnection( &clientParams);
  157. if(pNewClient)
  158. {
  159. if(!VerifyClient(pNewClient, psockAddrRemote))
  160. {
  161. DereferenceUserDataAndKill(pNewClient);
  162. pNewClient = NULL;
  163. fSockToBeFreed = FALSE;
  164. fDereferenceInstance = FALSE;
  165. SetLastError(ERROR_ACCESS_DENIED);
  166. }
  167. }
  168. }
  169. else
  170. {
  171. err = ERROR_REMOTE_SESSION_LIMIT_EXCEEDED;
  172. SetLastError(err);
  173. }
  174. if( pNewClient != NULL)
  175. {
  176. //DBGPRINTF((DBG_CONTEXT,"New connection object is non-null\n"));
  177. //
  178. // Start off processing this client connection.
  179. //
  180. // Once we make a reset call, the USER_DATA object is created
  181. // with the socket and atq context.
  182. // From now on USER_DATA will take care of freeing
  183. // ATQ context & socket
  184. //
  185. fSockToBeFreed = FALSE;
  186. //
  187. // At this point we have the context for the AcceptExed socket.
  188. // Set the context in the AtqContext if need be.
  189. //
  190. if ( patqContext != NULL)
  191. {
  192. //DBGPRINTF((DBG_CONTEXT,"AtqContext is not NULL\n"));
  193. //
  194. // Associate client connection object with this control socket
  195. // handle for future completions.
  196. //
  197. AtqContextSetInfo(patqContext,
  198. ATQ_INFO_COMPLETION_CONTEXT,
  199. (UINT_PTR) pNewClient);
  200. }
  201. else
  202. {
  203. //DBGPRINTF((DBG_CONTEXT,"AtqContext is NULL\n"));
  204. if(!pNewClient->AddToAtqHandles((HANDLE) sNew,EndpointObject, pInstance->QueryConnectionTimeout(),
  205. SmtpCompletion))
  206. {
  207. err = GetLastError();
  208. DBGPRINTF((DBG_CONTEXT,"AddToAtqHandles() failed- err= %d\n", err));
  209. DebugTrace((LPARAM) NULL, "pNewClient->AddToAtqHandles failed- err = %d", err);
  210. DereferenceUserDataAndKill(pNewClient);
  211. fDereferenceInstance = FALSE;
  212. fSockToBeFreed = FALSE;
  213. pNewClient = NULL;
  214. }
  215. }
  216. }
  217. else
  218. {
  219. err = GetLastError();
  220. if(err != ERROR_ACCESS_DENIED)
  221. fDereferenceInstance = TRUE;
  222. }
  223. if ( (pNewClient == NULL) || (err != NO_ERROR) )
  224. {
  225. // DBGPRINTF((DBG_CONTEXT,"New connection object is NULL\n"));
  226. //
  227. // Failed to allocate new connection
  228. // Reasons:
  229. // 1) Max connections might have been exceeded.
  230. // 2) Not enough memory is available.
  231. //
  232. // handle the failures and notify client.
  233. //
  234. HandleErrorCondition(sNew, pInstance, err, psockAddrRemote);
  235. }
  236. else
  237. {
  238. //DBGPRINTF((DBG_CONTEXT,"Calling StartSession()\n"));
  239. if(!pNewClient->StartSession())
  240. {
  241. err = GetLastError();
  242. DBGPRINTF((DBG_CONTEXT,"StartSession() failed - err= %d\n", err));
  243. DebugTrace((LPARAM) NULL, "pNewClient->StartSession() failed- err = %d", err);
  244. DereferenceUserDataAndKill(pNewClient);
  245. pNewClient = NULL;
  246. fSockToBeFreed = FALSE;
  247. fDereferenceInstance = FALSE;
  248. }
  249. else
  250. {
  251. fReturn = TRUE;
  252. }
  253. }
  254. if ( fSockToBeFreed )
  255. {
  256. if ( patqContext != NULL)
  257. {
  258. // ensure that socket is shut down.
  259. DBG_REQUIRE( AtqCloseSocket( patqContext, TRUE));
  260. }
  261. else
  262. {
  263. CloseSocket( sNew);
  264. }
  265. }
  266. if ( pfAtqToBeFreed != NULL)
  267. {
  268. *pfAtqToBeFreed = fSockToBeFreed;
  269. }
  270. if (pInstance && fDereferenceInstance )
  271. {
  272. pInstance->DecrementCurrentConnections();
  273. pInstance->Dereference();
  274. }
  275. TraceFunctLeaveEx((LPARAM) NULL);
  276. return (fReturn);
  277. } // ProcessNewClient()
  278. /*******************************************************************
  279. NAME: SmtpOnConnect
  280. SYNOPSIS: Handles the incoming connection indication from the
  281. connection thread
  282. ENTRY: sNew - New client socket
  283. HISTORY:
  284. KeithMo 09-Mar-1993 Created.
  285. Johnl 02-Aug-1994 Reworked from FTP server
  286. ********************************************************************/
  287. VOID SmtpOnConnect( IN SOCKET sNew,
  288. IN SOCKADDR_IN * psockaddr, //Should be SOCKADDR *
  289. IN PVOID pEndpointContext,
  290. IN PVOID pEndpointObject )
  291. {
  292. PIIS_ENDPOINT pEndpoint = (PIIS_ENDPOINT)pEndpointContext;
  293. INT cbAddr = sizeof( sockaddr );
  294. SOCKADDR_IN sockaddr;
  295. SMTP_SERVER_INSTANCE *pInstance;
  296. BOOL fProcessed;
  297. BOOL fMaxConnExceeded;
  298. DBG_ASSERT( sNew != INVALID_SOCKET );
  299. DBG_ASSERT( psockaddr != NULL );
  300. if ( g_pInetSvc->QueryCurrentServiceState() != SERVICE_RUNNING )
  301. {
  302. DBGPRINTF((DBG_CONTEXT,"Connection attempt on inactive service\n"));
  303. goto error_exit;
  304. }
  305. if ( getsockname( sNew, (PSOCKADDR) &sockaddr, &cbAddr ) != 0 )
  306. {
  307. goto error_exit;
  308. }
  309. //
  310. // Find Instance
  311. //
  312. pInstance = (SMTP_SERVER_INSTANCE *)
  313. ((PIIS_ENDPOINT)pEndpointContext)->FindAndReferenceInstance(
  314. (LPCSTR)NULL,
  315. sockaddr.sin_addr.s_addr,
  316. &fMaxConnExceeded);
  317. if ( pInstance == NULL )
  318. {
  319. //
  320. // Site is not permitted to access this server.
  321. // Dont establish this connection. We should send a message.
  322. //
  323. goto error_exit;
  324. }
  325. fProcessed = ProcessNewClient( sNew,
  326. pEndpointObject,
  327. pInstance,
  328. fMaxConnExceeded,
  329. psockaddr);
  330. if ( fProcessed)
  331. {
  332. //StatCheckAndSetMaxConnections();
  333. }
  334. return;
  335. error_exit:
  336. CloseSocket( sNew );
  337. return;
  338. } // SmtpOnConnect
  339. VOID
  340. SmtpOnConnectEx(
  341. VOID * patqContext,
  342. DWORD cbWritten,
  343. DWORD err,
  344. OVERLAPPED * lpo
  345. )
  346. {
  347. BOOL fAllowConnection = FALSE;
  348. PVOID pvBuff = NULL;
  349. PSOCKADDR_IN psockAddrLocal = NULL;
  350. PSOCKADDR_IN psockAddrRemote = NULL;
  351. SOCKET sNew;
  352. PIIS_ENDPOINT pEndpoint;
  353. PSMTP_SERVER_INSTANCE pInstance;
  354. BOOL fProcessed = FALSE;
  355. BOOL fAtqContextToBeFreed = TRUE;
  356. BOOL fMaxConnExceeded;
  357. if ( err || !lpo || g_IsShuttingDown)
  358. {
  359. if(g_IsShuttingDown)
  360. {
  361. DBGPRINTF(( DBG_CONTEXT,
  362. "[SmtpOnConnectEx] Completion failed because of shutdown %d, Atq context %lx\n",
  363. err,
  364. patqContext ));
  365. }
  366. else
  367. {
  368. DBGPRINTF(( DBG_CONTEXT,
  369. "[SmtpOnConnectEx] Completion failed with error %d, Atq context %lx\n",
  370. err,
  371. patqContext ));
  372. }
  373. goto exit;
  374. }
  375. //
  376. // Get AcceptEx parameters
  377. //
  378. AtqGetAcceptExAddrs( (PATQ_CONTEXT) patqContext,
  379. &sNew,
  380. &pvBuff,
  381. (PVOID*)&pEndpoint,
  382. (PSOCKADDR *) &psockAddrLocal,
  383. (PSOCKADDR *) &psockAddrRemote );
  384. if ( g_pInetSvc->QueryCurrentServiceState() != SERVICE_RUNNING )
  385. {
  386. DBGPRINTF((DBG_CONTEXT,"Connection attempt on inactive service\n"));
  387. goto exit ;
  388. }
  389. //
  390. // Find Instance
  391. //
  392. pInstance = (SMTP_SERVER_INSTANCE *)
  393. ((PIIS_ENDPOINT)pEndpoint)->FindAndReferenceInstance(
  394. (LPCSTR)NULL,
  395. psockAddrLocal->sin_addr.s_addr,
  396. &fMaxConnExceeded
  397. );
  398. if(pInstance == NULL)
  399. {
  400. //
  401. // Site is not permitted to access this server.
  402. // Dont establish this connection. We should send a message.
  403. //
  404. // DBGPRINTF((DBG_CONTEXT,
  405. // "Unable to find instance [err %d]\n",GetLastError()));
  406. goto exit;
  407. }
  408. //
  409. // Set the timeout for future IOs on this context
  410. //
  411. AtqContextSetInfo( (PATQ_CONTEXT) patqContext,
  412. ATQ_INFO_TIMEOUT,
  413. (UINT_PTR) pInstance->QueryConnectionTimeout());
  414. fProcessed = ProcessNewClient( sNew,
  415. pEndpoint,
  416. pInstance,
  417. fMaxConnExceeded,
  418. psockAddrRemote,
  419. psockAddrLocal,
  420. (PATQ_CONTEXT ) patqContext,
  421. pvBuff,
  422. cbWritten,
  423. &fAtqContextToBeFreed);
  424. exit:
  425. if ( !fProcessed && fAtqContextToBeFreed )
  426. {
  427. // DBGPRINTF((DBG_CONTEXT,
  428. // "ProcessNewClient returned false\n"));
  429. //
  430. // We failed to process this connection. Free up resources properly
  431. //
  432. DBG_REQUIRE( AtqCloseSocket( (PATQ_CONTEXT )patqContext, FALSE));
  433. AtqFreeContext( (PATQ_CONTEXT ) patqContext, TRUE );
  434. }
  435. return;
  436. } // SmtpOnConnectEx
  437. #if 0
  438. BOOL
  439. SendError(
  440. SOCKET socket,
  441. DWORD ids
  442. )
  443. {
  444. STR strResponse;
  445. if ( !strResponse.Resize( 512 ) ||
  446. !HTTP_REQ_BASE::BuildExtendedStatus( &strResponse,
  447. HT_FORBIDDEN,
  448. NO_ERROR,
  449. ids ))
  450. {
  451. DBGPRINTF((DBG_CONTEXT,
  452. "[SendError] Failed to build status (error %d)\n",
  453. GetLastError()));
  454. return FALSE;
  455. }
  456. //
  457. // Do a synchronous send
  458. //
  459. send( socket,
  460. strResponse.QueryStr(),
  461. strResponse.QueryCB(),
  462. 0 );
  463. return TRUE ;
  464. } // SendError
  465. #endif
  466. /*******************************************************************
  467. NAME: CloseSocket
  468. SYNOPSIS: Closes the specified socket. This is just a thin
  469. wrapper around the "real" closesocket() API.
  470. ENTRY: sock - The socket to close.
  471. RETURNS: SOCKERR - 0 if successful, !0 if not.
  472. HISTORY:
  473. KeithMo 26-Apr-1993 Created.
  474. ********************************************************************/
  475. SOCKERR CloseSocket( SOCKET sock )
  476. {
  477. SOCKERR serr = 0;
  478. //
  479. // Close the socket.
  480. //
  481. #if 0
  482. shutdown( sock, 1 ); // Davidtr sez not needed
  483. #endif
  484. if( closesocket( sock ) != 0 )
  485. {
  486. serr = WSAGetLastError();
  487. }
  488. if( serr == 0 )
  489. {
  490. // DBGPRINTF(( DBG_CONTEXT,
  491. // "closed socket %d\n",
  492. // sock ));
  493. }
  494. else
  495. {
  496. DBGPRINTF(( DBG_CONTEXT,
  497. "cannot close socket %d, error %d\n",
  498. sock,
  499. serr ));
  500. }
  501. return serr;
  502. } // CloseSocket
  503. /*++
  504. Description:
  505. Handles a completed IO.
  506. Arguments:
  507. pvContext: the context pointer specified in the initial IO
  508. cbWritten: the number of bytes sent
  509. dwCompletionStatus: the status of the completion (usually NO_ERROR)
  510. lpo: the overlapped structure associated with the IO
  511. Returns:
  512. nothing.
  513. --*/
  514. VOID
  515. SmtpCompletion(
  516. PVOID pvContext,
  517. DWORD cbWritten,
  518. DWORD dwCompletionStatus,
  519. OVERLAPPED * lpo
  520. )
  521. {
  522. BOOL WasProcessed;
  523. SMTP_CONNECTION *pCC = (SMTP_CONNECTION *) pvContext;
  524. _ASSERT(pCC);
  525. _ASSERT(pCC->IsValid());
  526. _ASSERT(pCC->QuerySmtpInstance() != NULL);
  527. //
  528. // if we could not process a command, or we were
  529. // told to destroy this object, close the connection.
  530. //
  531. WasProcessed = pCC->ProcessClient(cbWritten, dwCompletionStatus, lpo);
  532. }
  533. /*++
  534. Description:
  535. Handles a completed IO.
  536. Arguments:
  537. pvContext: the context pointer specified in the initial IO
  538. cbWritten: the number of bytes sent
  539. dwCompletionStatus: the status of the completion (usually NO_ERROR)
  540. lpo: the overlapped structure associated with the IO
  541. Returns:
  542. nothing.
  543. --*/
  544. VOID
  545. SmtpCompletionFIO(
  546. PFIO_CONTEXT pFIOContext,
  547. FH_OVERLAPPED *pOverlapped,
  548. DWORD cbWritten,
  549. DWORD dwCompletionStatus)
  550. {
  551. BOOL WasProcessed;
  552. SMTP_CONNECTION *pCC = (SMTP_CONNECTION *) (((SERVEREVENT_OVERLAPPED *) pOverlapped)->ThisPtr);
  553. _ASSERT(pCC);
  554. _ASSERT(pCC->IsValid());
  555. _ASSERT(pCC->QuerySmtpInstance() != NULL);
  556. //
  557. // if we could not process a command, or we were
  558. // told to destroy this object, close the connection.
  559. //
  560. WasProcessed = pCC->ProcessClient(cbWritten, dwCompletionStatus, (OVERLAPPED *) pOverlapped);
  561. }
  562. #if 0
  563. VOID
  564. ServerEventCompletion(
  565. PVOID pvContext,
  566. DWORD cbWritten,
  567. DWORD dwCompletionStatus,
  568. OVERLAPPED * lpo
  569. )
  570. {
  571. SERVEREVENT_OVERLAPPED * Ov = (SERVEREVENT_OVERLAPPED *) lpo;
  572. _ASSERT(pvContext);
  573. Ov->Overlapped.pfnCompletion(Ov->ThisPtr, cbWritten, dwCompletionStatus, lpo);
  574. }
  575. #endif