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.

847 lines
22 KiB

  1. /*++
  2. Copyright (c) 1992-1997 Microsoft Corporation
  3. Module Name:
  4. snmpthrd.c
  5. Abstract:
  6. Contains routines for master agent network thread.
  7. Environment:
  8. User Mode - Win32
  9. Revision History:
  10. 10-Feb-1997 DonRyan
  11. Rewrote to implement SNMPv2 support.
  12. --*/
  13. ///////////////////////////////////////////////////////////////////////////////
  14. // //
  15. // Include files //
  16. // //
  17. ///////////////////////////////////////////////////////////////////////////////
  18. #include <tchar.h>
  19. #include <stdio.h>
  20. #include "globals.h"
  21. #include "contexts.h"
  22. #include "regions.h"
  23. #include "snmpmgrs.h"
  24. #include "trapmgrs.h"
  25. #include "trapthrd.h"
  26. #include "network.h"
  27. #include "varbinds.h"
  28. #include "snmpmgmt.h"
  29. ///////////////////////////////////////////////////////////////////////////////
  30. // //
  31. // Global variables //
  32. // //
  33. ///////////////////////////////////////////////////////////////////////////////
  34. UINT g_nTransactionId = 0;
  35. ///////////////////////////////////////////////////////////////////////////////
  36. // //
  37. // Private definitions //
  38. // //
  39. ///////////////////////////////////////////////////////////////////////////////
  40. #define MAX_IPX_ADDR_LEN 64
  41. #define MAX_COMMUNITY_LEN 255
  42. #define ERRMSG_TRANSPORT_IP _T("IP")
  43. #define ERRMSG_TRANSPORT_IPX _T("IPX")
  44. ///////////////////////////////////////////////////////////////////////////////
  45. // //
  46. // Private procedures //
  47. // //
  48. ///////////////////////////////////////////////////////////////////////////////
  49. LPSTR
  50. AddrToString(
  51. struct sockaddr * pSockAddr
  52. )
  53. /*++
  54. Routine Description:
  55. Converts sockaddr to display string.
  56. Arguments:
  57. pSockAddr - pointer to socket address.
  58. Return Values:
  59. Returns pointer to string.
  60. --*/
  61. {
  62. static CHAR ipxAddr[MAX_IPX_ADDR_LEN];
  63. // determine family
  64. if (pSockAddr->sa_family == AF_INET) {
  65. struct sockaddr_in * pSockAddrIn;
  66. // obtain pointer to protocol specific structure
  67. pSockAddrIn = (struct sockaddr_in * )pSockAddr;
  68. // forward to winsock conversion function
  69. return inet_ntoa(pSockAddrIn->sin_addr);
  70. } else if (pSockAddr->sa_family == AF_IPX) {
  71. struct sockaddr_ipx * pSockAddrIpx;
  72. // obtain pointer to protocol specific structure
  73. pSockAddrIpx = (struct sockaddr_ipx * )pSockAddr;
  74. // transfer ipx address to static buffer
  75. sprintf(ipxAddr,
  76. "%02x%02x%02x%02x.%02x%02x%02x%02x%02x%02x",
  77. (BYTE)pSockAddrIpx->sa_netnum[0],
  78. (BYTE)pSockAddrIpx->sa_netnum[1],
  79. (BYTE)pSockAddrIpx->sa_netnum[2],
  80. (BYTE)pSockAddrIpx->sa_netnum[3],
  81. (BYTE)pSockAddrIpx->sa_nodenum[0],
  82. (BYTE)pSockAddrIpx->sa_nodenum[1],
  83. (BYTE)pSockAddrIpx->sa_nodenum[2],
  84. (BYTE)pSockAddrIpx->sa_nodenum[3],
  85. (BYTE)pSockAddrIpx->sa_nodenum[4],
  86. (BYTE)pSockAddrIpx->sa_nodenum[5]
  87. );
  88. // return addr
  89. return ipxAddr;
  90. }
  91. // failure
  92. return NULL;
  93. }
  94. LPSTR
  95. CommunityOctetsToString(
  96. AsnOctetString *pAsnCommunity,
  97. BOOL bUnicode
  98. )
  99. /*++
  100. Routine Description:
  101. Converts community octet string to display string.
  102. Arguments:
  103. pAsnCommunity - pointer to community octet string.
  104. Return Values:
  105. Returns pointer to string.
  106. --*/
  107. {
  108. static CHAR Community[MAX_COMMUNITY_LEN+1];
  109. LPSTR pCommunity = Community;
  110. // terminate string
  111. *pCommunity = '\0';
  112. // validate pointer
  113. if (pAsnCommunity != NULL)
  114. {
  115. DWORD nChars = 0;
  116. // determine number of characters to transfer
  117. nChars = min(pAsnCommunity->length, MAX_COMMUNITY_LEN);
  118. if (bUnicode)
  119. {
  120. WCHAR wCommunity[MAX_COMMUNITY_LEN+1];
  121. // tranfer memory into buffer
  122. memset(wCommunity, 0, nChars+sizeof(WCHAR));
  123. memcpy(wCommunity, pAsnCommunity->stream, nChars);
  124. SnmpUtilUnicodeToAnsi(&pCommunity, wCommunity, FALSE);
  125. }
  126. else
  127. {
  128. memcpy(Community, pAsnCommunity->stream, nChars);
  129. Community[nChars] = '\0';
  130. }
  131. }
  132. // success
  133. return pCommunity;
  134. }
  135. LPSTR
  136. StaticUnicodeToString(
  137. LPWSTR wszUnicode
  138. )
  139. /*++
  140. Routine Description:
  141. Converts null terminated UNICODE string to static LPSTR
  142. Arguments:
  143. pOctets - pointer to community octet string.
  144. Return Values:
  145. Returns pointer to string.
  146. --*/
  147. {
  148. static CHAR szString[MAX_COMMUNITY_LEN+1];
  149. LPSTR pszString = szString;
  150. // terminate string
  151. *pszString = '\0';
  152. // validate pointer
  153. if (wszUnicode != NULL)
  154. {
  155. WCHAR wcBreak;
  156. BOOL bNeedBreak;
  157. bNeedBreak = (wcslen(wszUnicode) > MAX_COMMUNITY_LEN);
  158. if (bNeedBreak)
  159. {
  160. wcBreak = wszUnicode[MAX_COMMUNITY_LEN];
  161. wszUnicode[MAX_COMMUNITY_LEN] = L'\0';
  162. }
  163. SnmpUtilUnicodeToAnsi(&pszString, wszUnicode, FALSE);
  164. if (bNeedBreak)
  165. wszUnicode[MAX_COMMUNITY_LEN] = wcBreak;
  166. }
  167. // success
  168. return pszString;
  169. }
  170. LPDWORD
  171. RefErrStatus(
  172. PSNMP_PDU pPdu
  173. )
  174. /*++
  175. Routine Description:
  176. Returns address of the Error Code in a PDU data structure
  177. Arguments:
  178. pPdu - PDU to check
  179. Return Values:
  180. Returns Error Code address, returns NULL if no Error Code
  181. --*/
  182. {
  183. switch (pPdu->nType) {
  184. case SNMP_PDU_GET:
  185. case SNMP_PDU_GETNEXT:
  186. case SNMP_PDU_SET:
  187. return &pPdu->Pdu.NormPdu.nErrorStatus;
  188. break;
  189. case SNMP_PDU_GETBULK:
  190. return &pPdu->Pdu.BulkPdu.nErrorStatus;
  191. break;
  192. default:
  193. return NULL;
  194. break;
  195. }
  196. }
  197. BOOL
  198. ValidateContext(
  199. PNETWORK_LIST_ENTRY pNLE
  200. )
  201. /*++
  202. Routine Description:
  203. Checks access rights of given context.
  204. Arguments:
  205. pNLE - pointer to network list entry.
  206. Return Values:
  207. Returns true if manager allowed access.
  208. --*/
  209. {
  210. BOOL fAccessOk = TRUE;
  211. BOOL fOk = FALSE;
  212. PCOMMUNITY_LIST_ENTRY pCLE = NULL;
  213. AsnOctetString unicodeCommunity;
  214. LPWSTR pUnicodeName;
  215. if (pNLE->Community.length != 0)
  216. {
  217. unicodeCommunity.length = pNLE->Community.length * sizeof(WCHAR);
  218. unicodeCommunity.stream = SnmpUtilMemAlloc(unicodeCommunity.length);
  219. unicodeCommunity.dynamic = TRUE;
  220. if (unicodeCommunity.stream == NULL)
  221. return FALSE;
  222. fAccessOk = (MultiByteToWideChar(
  223. CP_ACP,
  224. MB_PRECOMPOSED,
  225. pNLE->Community.stream,
  226. pNLE->Community.length,
  227. (LPWSTR)(unicodeCommunity.stream),
  228. unicodeCommunity.length) != 0);
  229. }
  230. else
  231. {
  232. unicodeCommunity.length = 0;
  233. unicodeCommunity.stream = NULL;
  234. unicodeCommunity.dynamic = FALSE;
  235. }
  236. // search for community string
  237. if (fAccessOk && FindValidCommunity(&pCLE, &unicodeCommunity))
  238. {
  239. // check access per pdu type
  240. if (pNLE->Pdu.nType == SNMP_PDU_SET) {
  241. // check flags for write privileges
  242. fAccessOk = (pCLE->dwAccess >= SNMP_ACCESS_READ_WRITE);
  243. } else {
  244. // check flags for read privileges
  245. fAccessOk = (pCLE->dwAccess >= SNMP_ACCESS_READ_ONLY);
  246. }
  247. if (!fAccessOk) {
  248. // Community does not have the right access
  249. // RefErrStatus returns a pointer to the ErrorStatus field from the SNMP_PDU structure.
  250. // It returns NULL if the PDU is in fact SNMP_TRAP_PDU. This doesn't happen here as far
  251. // as ValidateContext is called only after ParseMessage() which is filtering out
  252. // SNMP_TRAP_PDU.
  253. *RefErrStatus(&pNLE->Pdu) = SNMP_ERRORSTATUS_NOSUCHNAME;
  254. // register wrong operation for specified community into management structure
  255. mgmtCTick(CsnmpInBadCommunityUses);
  256. fOk = TRUE;
  257. }
  258. }
  259. else
  260. {
  261. fAccessOk = FALSE;
  262. // register community name failure into the management structure
  263. mgmtCTick(CsnmpInBadCommunityNames);
  264. }
  265. // see if access attempt should be logged
  266. if (!fAccessOk && snmpMgmtBase.AsnIntegerPool[IsnmpEnableAuthenTraps].asnValue.number) {
  267. // send authentication trap
  268. GenerateAuthenticationTrap();
  269. }
  270. SNMPDBG((
  271. SNMP_LOG_TRACE,
  272. "SNMP: SVC: %s request from community %s.\n",
  273. fAccessOk
  274. ? "accepting"
  275. : "rejecting"
  276. ,
  277. CommunityOctetsToString(&(pNLE->Community), FALSE)
  278. ));
  279. SnmpUtilOctetsFree(&unicodeCommunity);
  280. return (fOk || fAccessOk);
  281. }
  282. BOOL
  283. ValidateManager(
  284. PNETWORK_LIST_ENTRY pNLE
  285. )
  286. /*++
  287. Routine Description:
  288. Checks access rights of given manager.
  289. Arguments:
  290. pNLE - pointer to network list entry.
  291. Return Values:
  292. Returns true if manager allowed access.
  293. --*/
  294. {
  295. BOOL fAccessOk = FALSE;
  296. PMANAGER_LIST_ENTRY pMLE = NULL;
  297. fAccessOk = IsManagerAddrLegal((struct sockaddr_in *)&pNLE->SockAddr) &&
  298. (FindManagerByAddr(&pMLE, &pNLE->SockAddr) ||
  299. IsListEmpty(&g_PermittedManagers)
  300. );
  301. if (!fAccessOk &&
  302. snmpMgmtBase.AsnIntegerPool[IsnmpEnableAuthenTraps].asnValue.number)
  303. GenerateAuthenticationTrap();
  304. SNMPDBG((
  305. SNMP_LOG_TRACE,
  306. "SNMP: SVC: %s request from %s.\n",
  307. fAccessOk
  308. ? "accepting"
  309. : "rejecting"
  310. ,
  311. AddrToString(&pNLE->SockAddr)
  312. ));
  313. return fAccessOk;
  314. }
  315. BOOL
  316. ProcessSnmpMessage(
  317. PNETWORK_LIST_ENTRY pNLE
  318. )
  319. /*++
  320. Routine Description:
  321. Parse SNMP message and dispatch to subagents.
  322. Arguments:
  323. pNLE - pointer to network list entry.
  324. Return Values:
  325. Returns true if successful.
  326. --*/
  327. {
  328. BOOL fOk = FALSE;
  329. // decode request
  330. if (ParseMessage(
  331. &pNLE->nVersion,
  332. &pNLE->Community,
  333. &pNLE->Pdu,
  334. pNLE->Buffer.buf,
  335. pNLE->dwBytesTransferred
  336. )) {
  337. SNMPDBG((
  338. SNMP_LOG_TRACE,
  339. "SNMP: SVC: %s request, community %s, %d variable(s).\n",
  340. PDUTYPESTRING(pNLE->Pdu.nType),
  341. CommunityOctetsToString(&(pNLE->Community), FALSE),
  342. pNLE->Pdu.Vbl.len
  343. ));
  344. // validate context
  345. if (ValidateContext(pNLE)) {
  346. // process varbinds
  347. // RefErrStatus returns a pointer to the ErrorStatus field from SNMP_PDU structure.
  348. // The return value is NULL only if SNMP_PDU is in fact an SNMP_TRAP_PDU. Which is
  349. // not the case here, as far as it would have been filtered out by ParseMessage().
  350. if ((*RefErrStatus(&pNLE->Pdu) != SNMP_ERRORSTATUS_NOERROR) ||
  351. (ProcessVarBinds(pNLE))) {
  352. // initialize buffer length
  353. pNLE->Buffer.len = NLEBUFLEN;
  354. // reset pdu type to response
  355. pNLE->Pdu.nType = SNMP_PDU_RESPONSE;
  356. // encode response
  357. fOk = BuildMessage(
  358. pNLE->nVersion,
  359. &pNLE->Community,
  360. &pNLE->Pdu,
  361. pNLE->Buffer.buf,
  362. &pNLE->Buffer.len
  363. );
  364. }
  365. }
  366. }
  367. else {
  368. // register BER decoding failure into the management structures
  369. mgmtCTick(CsnmpInASNParseErrs);
  370. }
  371. // release pdu
  372. UnloadPdu(pNLE);
  373. return fOk;
  374. }
  375. void CALLBACK
  376. RecvCompletionRoutine(
  377. IN DWORD dwStatus,
  378. IN DWORD dwBytesTransferred,
  379. IN LPWSAOVERLAPPED pOverlapped,
  380. IN DWORD dwFlags
  381. )
  382. /*++
  383. Routine Description:
  384. Callback for completing asynchronous reads.
  385. Arguments:
  386. Status - completion status for the overlapped operation.
  387. BytesTransferred - number of bytes transferred.
  388. pOverlapped - pointer to overlapped structure.
  389. Flags - receive flags.
  390. Return Values:
  391. None.
  392. --*/
  393. {
  394. PNETWORK_LIST_ENTRY pNLE;
  395. EnterCriticalSection(&g_RegCriticalSectionA);
  396. // retreive pointer to network list entry from overlapped structure
  397. pNLE = CONTAINING_RECORD(pOverlapped, NETWORK_LIST_ENTRY, Overlapped);
  398. // copy receive completion information
  399. pNLE->nTransactionId = ++g_nTransactionId;
  400. pNLE->dwBytesTransferred = dwBytesTransferred;
  401. pNLE->dwStatus = dwStatus;
  402. pNLE->dwFlags = dwFlags;
  403. SNMPDBG((
  404. SNMP_LOG_TRACE,
  405. "SNMP: SVC: --- transaction %d begin ---\n",
  406. pNLE->nTransactionId
  407. ));
  408. // validate status
  409. if (dwStatus == NOERROR) {
  410. // register incoming packet into the management structure
  411. mgmtCTick(CsnmpInPkts);
  412. SNMPDBG((
  413. SNMP_LOG_TRACE,
  414. "SNMP: SVC: received %d bytes from %s.\n",
  415. pNLE->dwBytesTransferred,
  416. AddrToString(&pNLE->SockAddr)
  417. ));
  418. // check manager address
  419. if (ValidateManager(pNLE)) {
  420. // process snmp message
  421. if (ProcessSnmpMessage(pNLE)) {
  422. // synchronous send
  423. dwStatus = WSASendTo(
  424. pNLE->Socket,
  425. &pNLE->Buffer,
  426. 1,
  427. &pNLE->dwBytesTransferred,
  428. pNLE->dwFlags,
  429. &pNLE->SockAddr,
  430. pNLE->SockAddrLenUsed,
  431. NULL,
  432. NULL
  433. );
  434. // register outgoing packet into the management structure
  435. mgmtCTick(CsnmpOutPkts);
  436. // register outgoing Response PDU
  437. mgmtCTick(CsnmpOutGetResponses);
  438. // validate return code
  439. if (dwStatus != SOCKET_ERROR) {
  440. SNMPDBG((
  441. SNMP_LOG_TRACE,
  442. "SNMP: SVC: sent %d bytes to %s.\n",
  443. pNLE->dwBytesTransferred,
  444. AddrToString(&pNLE->SockAddr)
  445. ));
  446. } else {
  447. SNMPDBG((
  448. SNMP_LOG_ERROR,
  449. "SNMP: SVC: error %d sending response.\n",
  450. WSAGetLastError()
  451. ));
  452. }
  453. }
  454. }
  455. } else {
  456. SNMPDBG((
  457. SNMP_LOG_ERROR,
  458. "SNMP: SVC: error %d receiving snmp request.\n",
  459. dwStatus
  460. ));
  461. }
  462. SNMPDBG((
  463. SNMP_LOG_TRACE,
  464. "SNMP: SVC: --- transaction %d end ---\n",
  465. pNLE->nTransactionId
  466. ));
  467. LeaveCriticalSection(&g_RegCriticalSectionA);
  468. }
  469. ///////////////////////////////////////////////////////////////////////////////
  470. // //
  471. // Public procedures //
  472. // //
  473. ///////////////////////////////////////////////////////////////////////////////
  474. DWORD
  475. ProcessSnmpMessages(
  476. PVOID pParam
  477. )
  478. /*++
  479. Routine Description:
  480. Thread procedure for processing SNMP PDUs.
  481. Arguments:
  482. pParam - unused.
  483. Return Values:
  484. Returns true if successful.
  485. --*/
  486. {
  487. DWORD dwStatus;
  488. PLIST_ENTRY pLE;
  489. PNETWORK_LIST_ENTRY pNLE;
  490. SNMPDBG((
  491. SNMP_LOG_TRACE,
  492. "SNMP: SVC: Loading Registry Parameters.\n"
  493. ));
  494. // fire cold start trap
  495. GenerateColdStartTrap();
  496. SNMPDBG((
  497. SNMP_LOG_TRACE,
  498. "SNMP: SVC: starting pdu processing thread.\n"
  499. ));
  500. ReportSnmpEvent(
  501. SNMP_EVENT_SERVICE_STARTED,
  502. 0,
  503. NULL,
  504. 0);
  505. // loop
  506. for (;;)
  507. {
  508. // obtain pointer to first transport
  509. pLE = g_IncomingTransports.Flink;
  510. // loop through incoming transports
  511. while (pLE != &g_IncomingTransports)
  512. {
  513. // retreive pointer to network list entry from link
  514. pNLE = CONTAINING_RECORD(pLE, NETWORK_LIST_ENTRY, Link);
  515. // make sure recv is not pending
  516. if (pNLE->dwStatus != WSA_IO_PENDING)
  517. {
  518. // reset completion status
  519. pNLE->dwStatus = WSA_IO_PENDING;
  520. // intialize address structure size
  521. pNLE->SockAddrLenUsed = pNLE->SockAddrLen;
  522. // initialize buffer length
  523. pNLE->Buffer.len = NLEBUFLEN;
  524. // re-initialize
  525. pNLE->dwFlags = 0;
  526. // post receive buffer
  527. dwStatus = WSARecvFrom(
  528. pNLE->Socket,
  529. &pNLE->Buffer,
  530. 1, // dwBufferCount
  531. &pNLE->dwBytesTransferred,
  532. &pNLE->dwFlags,
  533. &pNLE->SockAddr,
  534. &pNLE->SockAddrLenUsed,
  535. &pNLE->Overlapped,
  536. RecvCompletionRoutine
  537. );
  538. // handle network failures
  539. if (dwStatus == SOCKET_ERROR)
  540. {
  541. // retrieve last error
  542. dwStatus = WSAGetLastError();
  543. // if WSA_IO_PENDING everything is ok, just waiting for incoming traffic. Otherwise...
  544. if (dwStatus != WSA_IO_PENDING)
  545. {
  546. // WSAECONNRESET means the last 'WSASendTo' (the one from RecvCompletionRoutine) failed
  547. // most probably because the manager closed the socket (so we got back 'unreacheable destination port')
  548. if (dwStatus == WSAECONNRESET)
  549. {
  550. SNMPDBG((
  551. SNMP_LOG_ERROR,
  552. "SNMP: SVC: Benign error %d posting receive buffer. Retry...\n",
  553. dwStatus
  554. ));
  555. // just go one more time and setup the port. It shouldn't ever loop continuously
  556. // and hence hog the CPU..
  557. pNLE->dwStatus = ERROR_SUCCESS;
  558. continue;
  559. }
  560. else
  561. {
  562. // prepare the event log insertion string
  563. LPTSTR pMessage = (pNLE->SockAddr.sa_family == AF_INET) ?
  564. ERRMSG_TRANSPORT_IP :
  565. ERRMSG_TRANSPORT_IPX;
  566. // another error occurred. We don't know how to handle it so it is a fatal
  567. // error for this transport. Will shut it down.
  568. SNMPDBG((
  569. SNMP_LOG_ERROR,
  570. "SNMP: SVC: Fatal error %d posting receive buffer. Skip transport.\n",
  571. dwStatus
  572. ));
  573. ReportSnmpEvent(
  574. SNMP_EVNT_INCOMING_TRANSPORT_CLOSED,
  575. 1,
  576. &pMessage,
  577. dwStatus);
  578. // first step next with the pointer
  579. pLE = pLE->Flink;
  580. // delete this transport from the incoming transports list
  581. UnloadTransport(pNLE);
  582. // go on further
  583. continue;
  584. }
  585. }
  586. }
  587. }
  588. pLE = pLE->Flink;
  589. }
  590. // we might want to shut the service down if no incoming transport remains.
  591. // we might as well consider letting the service up in order to keep sending outgoing traps.
  592. // for now, keep the service up (code below commented)
  593. //if (IsListEmpty(&g_IncomingTransports))
  594. //{
  595. // ReportSnmpEvent(...);
  596. // ProcessControllerRequests(SERVICE_CONTROL_STOP);
  597. //}
  598. // wait for incoming requests or indication of process termination
  599. dwStatus = WaitForSingleObjectEx(g_hTerminationEvent, INFINITE, TRUE);
  600. // validate return code
  601. if (dwStatus == WAIT_OBJECT_0) {
  602. SNMPDBG((
  603. SNMP_LOG_TRACE,
  604. "SNMP: SVC: exiting pdu processing thread.\n"
  605. ));
  606. // success
  607. return NOERROR;
  608. } else if (dwStatus != WAIT_IO_COMPLETION) {
  609. // retrieve error
  610. dwStatus = GetLastError();
  611. SNMPDBG((
  612. SNMP_LOG_ERROR,
  613. "SNMP: SVC: error %d waiting for request.\n",
  614. dwStatus
  615. ));
  616. // failure
  617. return dwStatus;
  618. }
  619. }
  620. }