Leaked source code of windows server 2003
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.

1598 lines
47 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. msgapi.c
  5. Abstract:
  6. Provides API functions for the messaging system.
  7. Author:
  8. Dan Lafferty (danl) 23-Jul-1991
  9. Environment:
  10. User Mode -Win32
  11. Notes:
  12. optional-notes
  13. Revision History:
  14. 02-Sep-1993 wlees
  15. Provide synchronization between rpc routines and Pnp reconfiguration
  16. 13-Jan-1993 danl
  17. NetrMessageNameGetInfo: Allocation size calculation was incorrectly
  18. trying to take the sizeof((NCBNAMSZ+1)*sizeof(WCHAR)). NCBNAMSZ is
  19. a #define constant value.
  20. 22-Jul-1991 danl
  21. Ported from LM2.0
  22. --*/
  23. //
  24. // Includes
  25. //
  26. #include "msrv.h"
  27. #include <tstring.h> // Unicode string macros
  28. #include <lmmsg.h>
  29. #include <netlib.h> // UNUSED macro
  30. #include <netlibnt.h> // NetpNtStatusToApiStatus
  31. #include <msgrutil.h> // NetpNetBiosReset
  32. #include <rpc.h>
  33. #include <msgsvc.h> // MIDL generated header file
  34. #include "msgdbg.h" // MSG_LOG
  35. #include "heap.h"
  36. #include "msgdata.h"
  37. #include "apiutil.h"
  38. #include "msgsec.h" // Messenger Security Information
  39. #include "msgsvcsend.h" // Broadcast message send interface
  40. // Static data descriptor strings for remoting the Message APIs
  41. static char nulstr[] = "";
  42. NET_API_STATUS
  43. NetrMessageNameEnum(
  44. IN LPWSTR ServerName,
  45. IN OUT LPMSG_ENUM_STRUCT InfoStruct,
  46. IN DWORD PrefMaxLen,
  47. OUT LPDWORD TotalEntries,
  48. IN OUT LPDWORD ResumeHandle OPTIONAL
  49. )
  50. /*++
  51. Routine Description:
  52. This function provides information about the message service name table
  53. at two levels of detail.
  54. Arguments:
  55. ServerName - Pointer to a string containing the name of the computer
  56. that is to execute the API function.
  57. InfoStruct - Pointer to a structure that contains the information that
  58. RPC needs about the returned data. This structure contains the
  59. following information:
  60. Level - The desired information level - indicates how to
  61. interpret the structure of the returned buffer.
  62. EntriesRead - Indicates how many elements are returned in the
  63. array of structures that are returned.
  64. BufferPointer - Location for the pointer to the array of
  65. structures that are being returned.
  66. PrefMaxLen - Indicates a maximum size limit that the caller will allow
  67. for the return buffer.
  68. TotalEntries - Pointer to a value that upon return indicates the total
  69. number of entries in the "active" database.
  70. ResumeHandle - Inidcates where in the linked list to start the
  71. enumeration. This is an optional parameter and can be NULL.
  72. Return Value:
  73. NERR_Success - The operation was successful. EntriesRead is valid.
  74. ERROR_INVALID_LEVEL - An invalid info level was passed in.
  75. ERROR_MORE_DATA - Not all the information in the database could be
  76. returned due to the limititation placed on buffer size by
  77. PrefMaxLen. One or more information records will be found in
  78. the buffer. EntriesRead is valid.
  79. ERROR_SERVICE_NOT_ACTIVE - The service is stopping.
  80. NERR_BufTooSmall - The limitation (PrefMaxLen) on buffer size didn't
  81. allow any information to be returned. Not even a single record
  82. could be fit in a buffer that small.
  83. NERR_InternalError - A name in the name table could not be translated
  84. from ansi characters to unicode characters. (Note: this
  85. currently causes 0 entries to be returned.)
  86. --*/
  87. {
  88. DWORD hResume = 0; // resume handle value
  89. DWORD entriesRead = 0;
  90. DWORD retBufSize;
  91. LPBYTE infoBuf;
  92. LPBYTE infoBufTemp;
  93. LPBYTE stringBuf;
  94. DWORD entry_length; // Length of one name entry in buf
  95. DWORD i,j,k; // index for name loop and flags
  96. NET_API_STATUS status=0;
  97. DWORD neti; // net index
  98. ULONG SessionId = 0; // client session Id
  99. DWORD dwMsgrState = GetMsgrState();
  100. UNUSED (ServerName);
  101. if (dwMsgrState == STOPPING || dwMsgrState == STOPPED) {
  102. return ERROR_SERVICE_NOT_ACTIVE;
  103. }
  104. //
  105. // Synchronize with Pnp configuration routine
  106. //
  107. MsgConfigurationLock(MSG_GET_SHARED,"NetrMessageNameEnum");
  108. //
  109. // If ResumeHandle is present and valid, initialize it.
  110. //
  111. if (ARGUMENT_PRESENT(ResumeHandle) && (*ResumeHandle < NCBMAX(0))) {
  112. hResume = *ResumeHandle;
  113. }
  114. //
  115. // In Hydra case, the display thread never goes asleep.
  116. //
  117. if (!g_IsTerminalServer)
  118. {
  119. //
  120. // Wakeup the display thread so that any queue'd messages can be
  121. // displayed.
  122. //
  123. MsgDisplayThreadWakeup();
  124. }
  125. //
  126. // Initialize some of the return counts.
  127. //
  128. *TotalEntries = 0;
  129. //
  130. // API security check. This call can be called by anyone locally,
  131. // but only by admins in the remote case.
  132. //
  133. status = NetpAccessCheckAndAudit(
  134. SERVICE_MESSENGER, // Subsystem Name
  135. (LPWSTR)MESSAGE_NAME_OBJECT, // Object Type Name
  136. MessageNameSd, // Security Descriptor
  137. MSGR_MESSAGE_NAME_ENUM, // Desired Access
  138. &MsgMessageNameMapping); // Generic Mapping
  139. if (status != NERR_Success) {
  140. MSG_LOG(TRACE,
  141. "NetrMessageNameEnum:NetpAccessCheckAndAudit FAILED %X\n",
  142. status);
  143. status = ERROR_ACCESS_DENIED;
  144. goto exit;
  145. }
  146. if (g_IsTerminalServer)
  147. {
  148. // get the client session id
  149. status = MsgGetClientSessionId(&SessionId);
  150. if (status != NERR_Success) {
  151. MSG_LOG(TRACE,
  152. "NetrMessageNameEnum:Could not get client session Id \n",0);
  153. goto exit;
  154. }
  155. }
  156. //
  157. // Determine the size of one element in the returned array.
  158. //
  159. switch( InfoStruct->Level) {
  160. case 0:
  161. if (InfoStruct->MsgInfo.Level0 == NULL)
  162. {
  163. status = ERROR_INVALID_PARAMETER;
  164. goto exit;
  165. }
  166. entry_length = sizeof(MSG_INFO_0);
  167. break;
  168. case 1:
  169. if (InfoStruct->MsgInfo.Level0 == NULL)
  170. {
  171. status = ERROR_INVALID_PARAMETER;
  172. goto exit;
  173. }
  174. entry_length = sizeof(MSG_INFO_1);
  175. break;
  176. default:
  177. status = ERROR_INVALID_LEVEL;
  178. goto exit;
  179. }
  180. //
  181. // Allocate enough space for return buffer
  182. //
  183. if (PrefMaxLen == -1) {
  184. //
  185. // If the caller has not specified a size, calculate a size
  186. // that will hold the entire enumeration.
  187. //
  188. retBufSize =
  189. ((NCBMAX(0) * ((NCBNAMSZ+1) * sizeof(WCHAR))) + // max possible num strings
  190. (NCBMAX(0) * entry_length)); // max possible num structs
  191. }
  192. else {
  193. retBufSize = PrefMaxLen;
  194. }
  195. infoBuf = (LPBYTE)MIDL_user_allocate(retBufSize);
  196. if (infoBuf == NULL) {
  197. status = ERROR_NOT_ENOUGH_MEMORY;
  198. goto exit;
  199. }
  200. stringBuf = infoBuf + (retBufSize & ~1); // & ~1 to align Unicode strings
  201. //
  202. // Block until data free
  203. //
  204. MsgDatabaseLock(MSG_GET_EXCLUSIVE,"NetrMessageNameEnum");
  205. //
  206. // Now copy as many names from the shared data name table as will fit
  207. // into the callers buffer. The shared data is locked so that the name
  208. // table can not change while it is being copied (eg by someone
  209. // deleting a forwarded name on this station after the check for a valid
  210. // name has been made but before the name has been read). The level 1
  211. // information is not copied in this loop as it requires network
  212. // activity which must be avoided while the shared data is locked.
  213. //
  214. //
  215. // HISTORY:
  216. //
  217. // The original LM2.0 code looked at the names on all nets, and
  218. // threw away duplicate ones. This implies that a name may appear
  219. // on one net and not on another. Although, this can never happen if
  220. // the names are always added via NetMessageNameAdd since that API
  221. // will not add the name unless it can be added to all nets. However,
  222. // forwarded names are added via a network receive, and may be added
  223. // from one net only.
  224. //
  225. // Since NT is not supporting forwarding, it is no longer necessary to
  226. // check each net. Since the only way to add names if via NetServiceAdd,
  227. // this will assure that the name listed for one network are the same
  228. // as the names listed for the others.
  229. //
  230. infoBufTemp = infoBuf;
  231. neti=j=0;
  232. status = NERR_Success;
  233. for(i=hResume; (i<NCBMAX(neti)) && (status==NERR_Success); ++i) {
  234. if (!(SD_NAMEFLAGS(neti,i) & (NFDEL | NFDEL_PENDING))) {
  235. //
  236. // in HYDRA case, we also consider the SessionId
  237. //
  238. if ((g_IsTerminalServer) && (!(MsgIsSessionInList(&(SD_SIDLIST(neti,i)), SessionId )))) {
  239. continue;
  240. }
  241. //
  242. // If a name is found we put it in the buffer if the
  243. // following conditions are met. If we are processing
  244. // the first net's names, put it in, it cannot be a
  245. // duplicate. Otherwise, only put it in if it is not
  246. // a duplicate of a name that is already in the user
  247. // buffer.
  248. // (NT_NOTE: duplicate names cannot occur).
  249. //
  250. //
  251. // translate the name to unicode and put it into the buffer
  252. //
  253. status = MsgGatherInfo (
  254. InfoStruct->Level,
  255. SD_NAMES(neti,i),
  256. &infoBufTemp,
  257. &stringBuf);
  258. if (status == NERR_Success) {
  259. entriesRead++;
  260. hResume++;
  261. }
  262. }
  263. }
  264. //
  265. // Calculate the total number of entries by seeing how many names are
  266. // left in the table and adding that to the entries read.
  267. //
  268. if (status == ERROR_NOT_ENOUGH_MEMORY) {
  269. status = ERROR_MORE_DATA;
  270. for (k=0; i < NCBMAX(neti); i++) {
  271. if(!(SD_NAMEFLAGS(neti,i) & (NFDEL | NFDEL_PENDING))) {
  272. k++;
  273. }
  274. }
  275. *TotalEntries = k;
  276. }
  277. *TotalEntries += entriesRead;
  278. //
  279. // Free up the shared data table
  280. //
  281. MsgDatabaseLock(MSG_RELEASE,"NetrMessageNameEnum");
  282. //
  283. // If some unexpected error occured, ( couldn't unformat the name
  284. // - or a bogus info level was passed in), then return the error.
  285. //
  286. if ( ! ((status == NERR_Success) || (status == ERROR_MORE_DATA)) ) {
  287. MIDL_user_free(infoBuf);
  288. infoBuf = NULL;
  289. entriesRead = 0;
  290. hResume = 0;
  291. goto exit;
  292. }
  293. //
  294. // if there were no entries read then either there were no more
  295. // entries in the table, or the resume number was bogus.
  296. // In this case, we want to free the allocated buffer storage.
  297. //
  298. if (entriesRead == 0) {
  299. MIDL_user_free(infoBuf);
  300. infoBuf = NULL;
  301. entriesRead = 0;
  302. hResume = 0;
  303. status = NERR_Success;
  304. if (*TotalEntries > 0) {
  305. status = NERR_BufTooSmall;
  306. }
  307. }
  308. //
  309. // If we have finished enumerating everything, reset the resume
  310. // handle to start at the beginning next time.
  311. //
  312. if (entriesRead == *TotalEntries) {
  313. hResume = 0;
  314. }
  315. //
  316. // Load up the information to return
  317. //
  318. switch(InfoStruct->Level) {
  319. case 0:
  320. InfoStruct->MsgInfo.Level0->EntriesRead = entriesRead;
  321. InfoStruct->MsgInfo.Level0->Buffer = (PMSG_INFO_0)infoBuf;
  322. break;
  323. case 1:
  324. InfoStruct->MsgInfo.Level0->EntriesRead = entriesRead;
  325. InfoStruct->MsgInfo.Level0->Buffer = (PMSG_INFO_0)infoBuf;
  326. break;
  327. default:
  328. //
  329. // This was checked above
  330. //
  331. ASSERT(FALSE);
  332. }
  333. if (ARGUMENT_PRESENT(ResumeHandle)) {
  334. *ResumeHandle = hResume;
  335. }
  336. exit:
  337. MsgConfigurationLock(MSG_RELEASE,"NetrMessageNameEnum");
  338. return (status);
  339. }
  340. NET_API_STATUS
  341. NetrMessageNameGetInfo(
  342. IN LPWSTR ServerName, // unicode server name, NULL if local
  343. IN LPWSTR Name, // Ptr to asciz name to query
  344. IN DWORD Level, // Level of detail requested
  345. OUT LPMSG_INFO InfoStruct // Ptr to buffer for info
  346. )
  347. /*++
  348. Routine Description:
  349. This funtion provides forwarding information about a known message server
  350. name table entry. However, since we do not support forwarding in NT,
  351. this API is totally useless. We'll support it anyway though for
  352. compatibility purposes.
  353. Arguments:
  354. ServerName - Pointer to a string containing the name of the computer
  355. that is to execute the API function.
  356. Name - The Messaging name that we are to get info on.
  357. Level - The level of information desired
  358. InfoStruct - Pointer to a location where the pointer to the returned
  359. information structure is to be placed.
  360. Return Value:
  361. --*/
  362. {
  363. NET_API_STATUS status=NERR_Success;
  364. LPMSG_INFO_0 infoBuf0;
  365. LPMSG_INFO_1 infoBuf1;
  366. CHAR formattedName[NCBNAMSZ];
  367. ULONG SessionId = 0; // Client Session Id
  368. DWORD dwMsgrState = GetMsgrState();
  369. UNUSED (ServerName);
  370. if (dwMsgrState == STOPPING || dwMsgrState == STOPPED)
  371. {
  372. return ERROR_SERVICE_NOT_ACTIVE;
  373. }
  374. if (MsgIsValidMsgName(Name) != 0)
  375. {
  376. return ERROR_INVALID_NAME;
  377. }
  378. //
  379. // Synchronize with Pnp configuration routine
  380. //
  381. MsgConfigurationLock(MSG_GET_SHARED,"NetrMessageNameGetInfo");
  382. //
  383. // In Hydra case, the display thread never goes asleep.
  384. //
  385. if (!g_IsTerminalServer)
  386. {
  387. //
  388. // Wakeup the display thread so that any queue'd messages can be
  389. // displayed.
  390. //
  391. MsgDisplayThreadWakeup();
  392. }
  393. //
  394. // API security check. This call can be called by anyone locally,
  395. // but only by admins in the remote case.
  396. //
  397. status = NetpAccessCheckAndAudit(
  398. SERVICE_MESSENGER, // Subsystem Name
  399. (LPWSTR)MESSAGE_NAME_OBJECT, // Object Type Name
  400. MessageNameSd, // Security Descriptor
  401. MSGR_MESSAGE_NAME_INFO_GET, // Desired Access
  402. &MsgMessageNameMapping); // Generic Mapping
  403. if (status != NERR_Success) {
  404. MSG_LOG(TRACE,
  405. "NetrMessageNameGetInfo:NetpAccessCheckAndAudit FAILED %X\n",
  406. status);
  407. status = ERROR_ACCESS_DENIED;
  408. goto exit;
  409. }
  410. //
  411. // Format the name so it matches what is stored in the name table.
  412. //
  413. status = MsgFmtNcbName(formattedName, Name, NAME_LOCAL_END);
  414. if (status != NERR_Success) {
  415. MSG_LOG(ERROR,"NetrMessageGetInfo: could not format name\n",0);
  416. status = NERR_NotLocalName;
  417. goto exit;
  418. }
  419. status = NERR_Success;
  420. //
  421. // Look for the name in the shared data name array. (1st net only).
  422. //
  423. if (g_IsTerminalServer)
  424. {
  425. // get the client session id
  426. status = MsgGetClientSessionId(&SessionId);
  427. if (status != NERR_Success) {
  428. MSG_LOG(ERROR,"NetrMessageGetInfo: could not get session id\n",0);
  429. goto exit;
  430. }
  431. }
  432. // look for the name in the database
  433. if (MsgLookupNameForThisSession(0, formattedName, SessionId) == -1) {
  434. MSG_LOG(ERROR,"NetrMessageGetInfo: Name not in table\n",0);
  435. status = NERR_NotLocalName;
  436. goto exit;
  437. }
  438. //
  439. // Allocate storage for the returned buffer, and fill it in.
  440. //
  441. switch(Level) {
  442. case 0:
  443. infoBuf0 = (LPMSG_INFO_0)MIDL_user_allocate(
  444. sizeof(MSG_INFO_0) + ((NCBNAMSZ+1)*sizeof(WCHAR)));
  445. if (infoBuf0 == NULL) {
  446. MSG_LOG(ERROR,
  447. "NetrMessageNameGetInfo MIDL allocate FAILED %X\n",
  448. GetLastError());
  449. status = ERROR_NOT_ENOUGH_MEMORY;
  450. goto exit;
  451. }
  452. //
  453. // copy the name and set the pointer in the structure to point
  454. // to it.
  455. //
  456. STRCPY((LPWSTR)(infoBuf0 + 1), Name);
  457. infoBuf0->msgi0_name = (LPWSTR)(infoBuf0 + 1);
  458. (*InfoStruct).MsgInfo0 = infoBuf0;
  459. break;
  460. case 1:
  461. infoBuf1 = (LPMSG_INFO_1)MIDL_user_allocate(
  462. sizeof(MSG_INFO_1) + ((NCBNAMSZ+1)*sizeof(WCHAR)) );
  463. if (infoBuf1 == NULL) {
  464. MSG_LOG(ERROR,
  465. "NetrMessageNameGetInfo MIDL allocate FAILED %X\n",
  466. GetLastError());
  467. status = ERROR_NOT_ENOUGH_MEMORY;
  468. goto exit;
  469. }
  470. //
  471. // Copy the name, update pointers, and set forward info fields.
  472. //
  473. STRCPY((LPWSTR)(infoBuf1 + 1), Name);
  474. infoBuf1->msgi1_name = (LPWSTR)(infoBuf1 + 1);
  475. infoBuf1->msgi1_forward_flag = 0;
  476. infoBuf1->msgi1_forward = NULL;
  477. (*InfoStruct).MsgInfo1 = infoBuf1;
  478. break;
  479. default:
  480. status = ERROR_INVALID_LEVEL;
  481. goto exit;
  482. }
  483. status = NERR_Success;
  484. exit:
  485. MsgConfigurationLock(MSG_RELEASE,"NetrMessageNameGetInfo");
  486. return status;
  487. }
  488. NET_API_STATUS
  489. NetrMessageNameAdd(
  490. LPWSTR ServerName, // NULL = local
  491. LPWSTR Name // Pointer to name to add.
  492. )
  493. /*++
  494. Routine Description:
  495. This function performs a security check for all calls to this
  496. RPC interface. Then it adds a new name to the Message
  497. Server's name table by calling the MsgAddName function.
  498. Arguments:
  499. ServerName - Pointer to a string containing the name of the computer
  500. that is to execute the API function.
  501. Name - A pointer to the name to be added.
  502. Return Value:
  503. NERR_Success - The operation was successful.
  504. ERROR_ACCESS_DENIED - If the Security Check Failed.
  505. ERROR_SERVICE_NOT_ACTIVE - The service is stopping
  506. Assorted Error codes from MsgAddName.
  507. --*/
  508. {
  509. NET_API_STATUS status=0;
  510. ULONG SessionId = 0;
  511. DWORD dwMsgrState = GetMsgrState();
  512. UNUSED (ServerName);
  513. if (dwMsgrState == STOPPING || dwMsgrState == STOPPED) {
  514. return ERROR_SERVICE_NOT_ACTIVE;
  515. }
  516. //
  517. // Synchronize with Pnp configuration routine
  518. //
  519. MsgConfigurationLock(MSG_GET_SHARED,"NetrMessageNameAdd");
  520. //
  521. // API security check. This call can be called by anyone locally,
  522. // but only by admins in the remote case.
  523. //
  524. status = NetpAccessCheckAndAudit(
  525. SERVICE_MESSENGER, // Subsystem Name
  526. (LPWSTR)MESSAGE_NAME_OBJECT, // Object Type Name
  527. MessageNameSd, // Security Descriptor
  528. MSGR_MESSAGE_NAME_ADD, // Desired Access
  529. &MsgMessageNameMapping); // Generic Mapping
  530. if (status != NERR_Success) {
  531. MSG_LOG(TRACE,
  532. "NetrMessageNameAdd:NetpAccessCheckAndAudit FAILED %X\n",
  533. status);
  534. status = ERROR_ACCESS_DENIED;
  535. goto exit;
  536. }
  537. //
  538. // In Hydra case, the display thread never goes asleep.
  539. //
  540. if (!g_IsTerminalServer)
  541. {
  542. //
  543. // Since a new user may have just logged on, we want to check to see if
  544. // there are any messages to be displayd.
  545. //
  546. MsgDisplayThreadWakeup();
  547. }
  548. else
  549. {
  550. // get the client session id
  551. status = MsgGetClientSessionId(&SessionId);
  552. if (status != NERR_Success)
  553. {
  554. MSG_LOG(ERROR, "NetrMessageNameAdd: could not get client session id\n",0);
  555. goto exit;
  556. }
  557. }
  558. //
  559. // Call the function that actually adds the name.
  560. //
  561. MSG_LOG(TRACE, "NetrMessageNameAdd: call MsgAddName for Session %x\n",SessionId);
  562. status = MsgAddName(Name, SessionId);
  563. exit:
  564. MsgConfigurationLock(MSG_RELEASE,"NetrMessageNameAdd");
  565. return status;
  566. }
  567. NET_API_STATUS
  568. MsgAddName(
  569. LPWSTR Name,
  570. ULONG SessionId
  571. )
  572. /*++
  573. Routine Description:
  574. This function adds a new name to the Message Server's name table.
  575. It is available to be called internally (from within the Messenger
  576. service).
  577. The task of adding a new name to the Message Server's name table consists
  578. of verifying that a session can be established for a new name (Note: this
  579. check is subject to failure in a multiprocessing environment, since the
  580. state of the adapter may change between the time of the check and the time
  581. of the attempt to establish a session), verifying that the name does not
  582. already exist in the local name table, adding the name to the local adapter
  583. via an ADD NAME net bios call, adding the name to the Message Server's name
  584. table and marking it as new, waking up the Message Server using the wakeup
  585. semaphore, and checking to see if messages for the new name have been
  586. forwarded (if they have been forwarded, the value of the fwd_action
  587. flag is used to determine the action to be taken).
  588. SIDE EFFECTS
  589. Calls the net bios. May modify the Message Server's shared data area.
  590. May call DosSemClear() on the wakeup semaphore.
  591. Arguments:
  592. Name - A pointer to the name to be added.
  593. Return Value:
  594. NERR_Success - The operation was successful.
  595. assorted errors.
  596. --*/
  597. {
  598. NCB ncb; // Network control block
  599. TCHAR namebuf[NCBNAMSZ+2]; // General purpose name buffer
  600. UCHAR net_err=0; // Storage for net error codes
  601. NET_API_STATUS err_code=0; // Storage for return error codes
  602. DWORD neti,i,name_i; // Index
  603. NET_API_STATUS status=0;
  604. if (MsgIsValidMsgName(Name) != 0)
  605. {
  606. return ERROR_INVALID_NAME;
  607. }
  608. MSG_LOG(TRACE,"Attempting to add the following name: %ws\n",Name);
  609. STRNCPY( namebuf, Name, NCBNAMSZ+1);
  610. namebuf[NCBNAMSZ+1] = '\0';
  611. //
  612. // Initialize the NCB
  613. //
  614. clearncb(&ncb);
  615. //
  616. // Format the name for NetBios.
  617. // This converts the Unicode string to ansi.
  618. //
  619. status = MsgFmtNcbName(ncb.ncb_name, namebuf, NAME_LOCAL_END);
  620. if (status != NERR_Success) {
  621. MSG_LOG(ERROR,"MsgAddName: could not format name\n",0);
  622. return (ERROR_INVALID_NAME);
  623. }
  624. //
  625. // Check if the local name already exists on any netcard
  626. // in this machine. This check does not mean the name dosn't
  627. // exist on some other machine on the network(s).
  628. //
  629. for ( neti = 0; neti < SD_NUMNETS(); neti++ ) {
  630. //
  631. // Gain access to the shared database.
  632. //
  633. MsgDatabaseLock(MSG_GET_EXCLUSIVE,"MsgAddName");
  634. for( i = 0, err_code = 0; i < 10; i++) {
  635. // check if this alias is not already existing for this session
  636. name_i = MsgLookupNameForThisSession(neti, ncb.ncb_name, SessionId);
  637. if ((name_i) == -1) {
  638. break;
  639. }
  640. if( (SD_NAMEFLAGS(neti,name_i) & NFDEL_PENDING) && (i < 9)) {
  641. //
  642. // Delete is pending so wait for it
  643. //
  644. Sleep(500L);
  645. }
  646. else {
  647. //
  648. // Setup error code
  649. //
  650. err_code = NERR_AlreadyExists;
  651. break;
  652. }
  653. }
  654. MsgDatabaseLock(MSG_RELEASE,"MsgAddName");
  655. if ( err_code == NERR_AlreadyExists ) {
  656. break;
  657. }
  658. }
  659. if( err_code == 0)
  660. {
  661. //
  662. // Either the name was not forwarded or the fwd_action flag
  663. // was set so go ahead and try to add the name to each net.
  664. //
  665. ncb.ncb_name[NCBNAMSZ - 1] = NAME_LOCAL_END;
  666. //
  667. // on each network
  668. //
  669. for ( neti = 0; neti < SD_NUMNETS(); neti++ ) {
  670. //
  671. // Gain access to the shared database.
  672. //
  673. MsgDatabaseLock(MSG_GET_EXCLUSIVE,"MsgAddName");
  674. if (g_IsTerminalServer)
  675. {
  676. // before looking for an empty slot,
  677. // check if this alias does not already exist for another session
  678. for( i = 0; i < 10; i++)
  679. {
  680. name_i = MsgLookupName(neti, ncb.ncb_name);
  681. if ((name_i != -1) && (SD_NAMEFLAGS(neti, name_i) & NFDEL_PENDING))
  682. {
  683. //
  684. // Delete is pending so wait for it
  685. //
  686. Sleep(500L);
  687. }
  688. else
  689. {
  690. break;
  691. }
  692. }
  693. if (name_i != -1)
  694. {
  695. if (SD_NAMEFLAGS(neti, name_i) & NFDEL_PENDING) // still there ?
  696. {
  697. err_code = NERR_InternalError; // what else can we do ?
  698. MsgDatabaseLock(MSG_RELEASE, "MsgAddName");
  699. break;
  700. }
  701. // this alias already exists for another session, so just add the session id in the list
  702. MSG_LOG(TRACE,"MsgAddName: Alias already existing. Just adding Session %x \n", SessionId);
  703. MsgAddSessionInList(&(SD_SIDLIST(neti, name_i)), SessionId); //There is not much we can do if this call fails
  704. MsgDatabaseLock(MSG_RELEASE, "MsgAddName");
  705. continue;
  706. }
  707. }
  708. for(i = 0; i < NCBMAX(neti); ++i)
  709. {
  710. //
  711. // Loop to find empty slot
  712. //
  713. if (SD_NAMEFLAGS(neti,i) & NFDEL)
  714. {
  715. //
  716. // If empty slot found, Lock slot in table and
  717. // end the search
  718. //
  719. SD_NAMEFLAGS(neti,i) = NFLOCK;
  720. MSG_LOG2(TRACE,"MsgAddName: Lock slot %d in table "
  721. "for net %d\n",i,neti);
  722. break;
  723. }
  724. }
  725. if ((i == NCBMAX(neti)) && (i < NCB_MAX_ENTRIES))
  726. {
  727. // We can add another NCB - must hold the lock.
  728. PNCB_DATA pNcbDataNew;
  729. PNET_DATA pNetData = GETNETDATA(neti);
  730. if (pNcbDataNew = (PNCB_DATA) LocalAlloc(LMEM_ZEROINIT,
  731. sizeof(NCB_DATA)))
  732. {
  733. // Initialize and lock the NCB
  734. pNcbDataNew->Ncb.ncb_cmd_cplt = 0xff;
  735. pNcbDataNew->NameFlags = NFLOCK;
  736. // Add the new NCB to the list
  737. pNetData->NcbList[i] = pNcbDataNew;
  738. //
  739. //create an empty session list
  740. //
  741. InitializeListHead(&(SD_SIDLIST(neti,i)));
  742. pNetData->NumNcbs++; // This must be done last
  743. }
  744. else
  745. {
  746. err_code = ERROR_NOT_ENOUGH_MEMORY;
  747. }
  748. }
  749. //
  750. // Unlock the shared database
  751. //
  752. MsgDatabaseLock(MSG_RELEASE, "MsgAddName");
  753. if( i >= NCBMAX(neti))
  754. {
  755. //
  756. // If no room in name table
  757. //
  758. err_code = NERR_TooManyNames;
  759. }
  760. else if (err_code == NERR_Success)
  761. {
  762. //
  763. // Send ADDNAME
  764. //
  765. ncb.ncb_command = NCBADDNAME; // Add name (wait)
  766. ncb.ncb_lana_num = GETNETLANANUM(neti);
  767. MSG_LOG1(TRACE,"MsgNameAdd: Calling sendncb for lana #%d...\n",
  768. GETNETLANANUM(neti));
  769. if ((net_err = Msgsendncb(&ncb,neti)) == 0)
  770. {
  771. MSG_LOG(TRACE,"MsgAddName: sendncb returned SUCCESS\n",0);
  772. //
  773. // successful add - Get the Lock.
  774. //
  775. MsgDatabaseLock(MSG_GET_EXCLUSIVE,"MsgAddName");
  776. //
  777. // Copy the name to shared memory
  778. //
  779. MSG_LOG3(TRACE,"MsgAddName: copy name (%s)\n\tto "
  780. "shared data table (net,loc)(%d,%d)\n",
  781. ncb.ncb_name, neti, i);
  782. memcpy(SD_NAMES(neti,i),ncb.ncb_name, NCBNAMSZ);
  783. //
  784. // Set the name no.
  785. //
  786. SD_NAMENUMS(neti,i) = ncb.ncb_num ;
  787. //
  788. // Set new name flag
  789. //
  790. SD_NAMEFLAGS(neti,i) = NFNEW;
  791. if (g_IsTerminalServer)
  792. {
  793. // Add the session id in the list
  794. MSG_LOG(TRACE,"MsgAddName: Alias created for Session %x \n", SessionId);
  795. MsgAddSessionInList(&(SD_SIDLIST(neti, i)), SessionId);
  796. // If this fails due to low memory, we would find the name in the list and messages will
  797. // not get deliviered. Doesn't cause any crashes. This is the best we can do
  798. }
  799. //
  800. // Unlock share table
  801. //
  802. MsgDatabaseLock(MSG_RELEASE, "MsgAddName");
  803. //
  804. // START A SESSION for this name.
  805. //
  806. err_code = MsgNewName(neti,i);
  807. if (err_code != NERR_Success) {
  808. MSG_LOG(TRACE, "MsgAddName: A Session couldn't be "
  809. "created for this name %d\n",err_code);
  810. MSG_LOG(TRACE,"MsgAddName: Delete the name "
  811. "that failed (%s)\n",ncb.ncb_name)
  812. ncb.ncb_command = NCBDELNAME;
  813. ncb.ncb_lana_num = GETNETLANANUM(i);
  814. net_err = Msgsendncb( &ncb, i);
  815. if (net_err != 0) {
  816. MSG_LOG(ERROR,"MsgAddName: Delete name "
  817. "failed %d - pretend it's deleted anyway\n",net_err);
  818. }
  819. //
  820. // Re-mark slot empty
  821. //
  822. SD_NAMEFLAGS(neti,i) = NFDEL;
  823. MSG_LOG2(TRACE,"MsgAddName: UnLock slot %d in table "
  824. "for net %d\n",i,neti);
  825. MSG_LOG(TRACE,"MsgAddName: Name Deleted\n",0)
  826. }
  827. else {
  828. //
  829. //
  830. // Wakeup the worker thread for that network.
  831. //
  832. SetEvent(wakeupSem[neti]);
  833. }
  834. }
  835. else {
  836. //
  837. // else set error code
  838. //
  839. MSG_LOG(TRACE,
  840. "MsgAddName: sendncb returned FAILURE 0x%x\n",
  841. net_err);
  842. err_code = MsgMapNetError(net_err);
  843. //
  844. // Re-mark slot empty
  845. //
  846. SD_NAMEFLAGS(neti,i) = NFDEL;
  847. MSG_LOG2(TRACE,"MsgAddName: UnLock slot %d in table "
  848. "for net %d\n",i,neti);
  849. }
  850. }
  851. if ( err_code != NERR_Success )
  852. {
  853. //
  854. //Try to delete the add names that were successful
  855. //
  856. for ( i = 0; i < neti; i++ )
  857. {
  858. MsgDatabaseLock(MSG_GET_EXCLUSIVE,"MsgAddName");
  859. // try to delete only the alias for this session
  860. name_i = MsgLookupNameForThisSession(i,
  861. (char far *)(ncb.ncb_name),
  862. SessionId);
  863. if (name_i == -1)
  864. {
  865. err_code = NERR_InternalError;
  866. MsgDatabaseLock(MSG_RELEASE, "MsgAddName");
  867. break;
  868. }
  869. if (g_IsTerminalServer)
  870. {
  871. // in any case remove the reference to the session
  872. MSG_LOG(TRACE,"MsgAddName: Removing Session %x from list\n", SessionId);
  873. MsgRemoveSessionFromList(&(SD_SIDLIST(i, name_i)), SessionId);
  874. }
  875. MsgDatabaseLock(MSG_RELEASE, "MsgAddName");
  876. // if it was the last session using this alias then delete the alias
  877. if ((!g_IsTerminalServer) || (IsListEmpty(&(SD_SIDLIST(i, name_i)))))
  878. {
  879. MSG_LOG(TRACE,"MsgAddName: Session list empty. Deleting the alias \n", 0);
  880. //
  881. // Delete name from card.
  882. // If this call fails, we can't do much about it.
  883. //
  884. MSG_LOG1(TRACE,"MsgAddName: Delete the name that failed "
  885. "for lana #%d\n",GETNETLANANUM(i))
  886. ncb.ncb_command = NCBDELNAME;
  887. ncb.ncb_lana_num = GETNETLANANUM(i);
  888. Msgsendncb( &ncb, i);
  889. //
  890. // Re-mark slot empty
  891. //
  892. SD_NAMEFLAGS(i,name_i) = NFDEL;
  893. MSG_LOG2(TRACE,"MsgAddName: UnLock slot %d in table "
  894. "for net %d\n",i,neti);
  895. }
  896. }
  897. //
  898. // If an add was unsuccessful, stop the loop
  899. //
  900. break;
  901. } // end else (err_code != NERR_Success)
  902. } // end add names to net loop
  903. } // end if ( !err_cd )
  904. MSG_LOG(TRACE,"MsgAddName: exit with err_code = %x\n",err_code);
  905. return(err_code);
  906. }
  907. NET_API_STATUS
  908. NetrMessageNameDel(
  909. IN LPWSTR ServerName, // Blank = local, else remote.
  910. IN LPWSTR Name // Pointer to name to be deleted
  911. )
  912. /*++
  913. Routine Description:
  914. This function deletes a name from the Message Server's name table.
  915. This function is called to delete a name that has been added by the
  916. user or by a remote computer via a Start Forwarding request to the
  917. Message Server. The user has no way of specifying whether the given
  918. name is an additional name or a forwarded name, but since forwarding
  919. of messages to one's own computer is prohibited, both forms of the
  920. name cannot exist on one machine (unless the message system has been
  921. circumvented--a simple enough thing to do). The given name is looked
  922. up in the shared data area, and, if it is found, a DELETE NAME net bios
  923. call is issued. If this call is successful, then the Message Server
  924. will remove the name from its name table in shared memory, so this
  925. function does not have to do so.
  926. SIDE EFFECTS
  927. Calls the net bios. Accesses the shared data area.
  928. Arguments:
  929. ServerName - Pointer to a string containing the name of the computer
  930. that is to execute the API function.
  931. Name - A pointer to the name to be deleted.
  932. Return Value:
  933. NERR_Success - The operation was successful.
  934. ERROR_SERVICE_NOT_ACTIVE - The service is stopping.
  935. --*/
  936. {
  937. NCB ncb; // Network control block
  938. DWORD flags; // Name flags
  939. DWORD i; // Index into name table
  940. DWORD neti; // Network Index
  941. NET_API_STATUS end_result;
  942. DWORD name_len;
  943. UCHAR net_err;
  944. ULONG SessionId = 0; // Client Session Id
  945. DWORD dwMsgrState = GetMsgrState();
  946. UNUSED (ServerName);
  947. if (dwMsgrState == STOPPING || dwMsgrState == STOPPED)
  948. {
  949. return ERROR_SERVICE_NOT_ACTIVE;
  950. }
  951. if (MsgIsValidMsgName(Name) != 0)
  952. {
  953. return ERROR_INVALID_NAME;
  954. }
  955. //
  956. // Synchronize with Pnp configuration routine
  957. //
  958. MsgConfigurationLock(MSG_GET_SHARED,"NetrMessageNameDel");
  959. //
  960. // In Hydra case, the display thread never goes asleep.
  961. //
  962. if (!g_IsTerminalServer)
  963. {
  964. //
  965. // Wakeup the display thread so that any queue'd messages can be
  966. // displayed.
  967. //
  968. MsgDisplayThreadWakeup();
  969. }
  970. //
  971. // API security check. This call can be called by anyone locally,
  972. // but only by admins in the remote case.
  973. //
  974. end_result = NetpAccessCheckAndAudit(
  975. SERVICE_MESSENGER, // Subsystem Name
  976. (LPWSTR) MESSAGE_NAME_OBJECT, // Object Type Name
  977. MessageNameSd, // Security Descriptor
  978. MSGR_MESSAGE_NAME_DEL, // Desired Access
  979. &MsgMessageNameMapping); // Generic Mapping
  980. if (end_result != NERR_Success)
  981. {
  982. MSG_LOG(ERROR,
  983. "NetrMessageNameDel:NetpAccessCheckAndAudit FAILED %d\n",
  984. end_result);
  985. goto exit;
  986. }
  987. //
  988. // Initialize the NCB
  989. //
  990. clearncb(&ncb);
  991. //
  992. // Format the username (this makes it non-unicode);
  993. //
  994. end_result = MsgFmtNcbName(ncb.ncb_name, Name, NAME_LOCAL_END);
  995. if (end_result != NERR_Success)
  996. {
  997. MSG_LOG(ERROR,
  998. "NetrMessageNameDel: could not format name %d\n",
  999. end_result);
  1000. goto exit;
  1001. }
  1002. if (g_IsTerminalServer)
  1003. {
  1004. end_result = MsgGetClientSessionId(&SessionId);
  1005. if (end_result != NERR_Success)
  1006. {
  1007. MSG_LOG(ERROR,
  1008. "NetrMessageNameDel: could not get session id %d\n",
  1009. end_result);
  1010. goto exit;
  1011. }
  1012. }
  1013. end_result = NERR_Success;
  1014. //
  1015. // for all nets
  1016. //
  1017. for ( neti = 0; neti < SD_NUMNETS(); neti++ ) {
  1018. //
  1019. // Block until data free
  1020. //
  1021. MsgDatabaseLock(MSG_GET_EXCLUSIVE,"NetrMessageNameDel");
  1022. name_len = STRLEN(Name);
  1023. if((name_len > NCBNAMSZ)
  1024. ||
  1025. ((i = MsgLookupNameForThisSession( neti, ncb.ncb_name, SessionId))) == -1)
  1026. {
  1027. MSG_LOG(TRACE,"NetrMessageNameDel: Alias not found for Session %x \n", SessionId);
  1028. //
  1029. // No such name to delete - exit
  1030. //
  1031. MsgDatabaseLock(MSG_RELEASE, "NetrMessageNameDel");
  1032. end_result = NERR_NotLocalName;
  1033. goto exit;
  1034. }
  1035. if (g_IsTerminalServer)
  1036. {
  1037. // remove the session id from the list
  1038. MSG_LOG(TRACE,"NetrMessageNameDel: Removing Session %x from list\n", SessionId);
  1039. MsgRemoveSessionFromList(&(SD_SIDLIST(neti,i)), SessionId);
  1040. }
  1041. //
  1042. // in Hydra case, if it is not the last session using this alias, do not delete the alias
  1043. //
  1044. if ((g_IsTerminalServer) && (!IsListEmpty(&(SD_SIDLIST(neti,i)))))
  1045. {
  1046. MSG_LOG(TRACE,"NetrMessageNameDel: Session list is not empty. Do not delete the alias\n", 0);
  1047. MsgDatabaseLock(MSG_RELEASE, "NetrMessageNameDel");
  1048. continue;
  1049. }
  1050. else
  1051. {
  1052. MSG_LOG(TRACE,"NetrMessageNameDel: Session list is empty. Deleting the alias\n", 0);
  1053. }
  1054. flags = SD_NAMEFLAGS(neti,i);
  1055. if(!(flags & (NFMACHNAME | NFLOCK))
  1056. &&
  1057. !(flags & NFFOR))
  1058. {
  1059. //
  1060. // Show delete pending
  1061. //
  1062. SD_NAMEFLAGS(neti,i) |= NFDEL_PENDING;
  1063. }
  1064. MsgDatabaseLock(MSG_RELEASE, "NetrMessageNameDel");
  1065. if (flags & NFMACHNAME)
  1066. {
  1067. //
  1068. // If name is computer name
  1069. //
  1070. end_result = NERR_DelComputerName;
  1071. goto exit;
  1072. }
  1073. if(flags & NFLOCK)
  1074. {
  1075. //
  1076. // If name is locked
  1077. //
  1078. end_result = NERR_NameInUse;
  1079. MSG_LOG(TRACE,"NetrMessageNameDel: Deleting a locked name is forbidden\n", 0);
  1080. goto exit;
  1081. }
  1082. //
  1083. // Delete the Name
  1084. //
  1085. ncb.ncb_command = NCBDELNAME; // Delete name (wait)
  1086. ncb.ncb_lana_num = GETNETLANANUM(neti);
  1087. if( (net_err = Msgsendncb( &ncb, neti)) != 0 )
  1088. {
  1089. MSG_LOG(ERROR,"NetrMessageNameDel:send NCBDELNAME failed 0x%x\n",
  1090. net_err);
  1091. //
  1092. // The name that has been marked as delete pending was not
  1093. // successfully deleted so now go through all the work of
  1094. // finding the name again (cannot even use the same index
  1095. // in case deleted by another process) and remove the
  1096. // Del pending flag
  1097. //
  1098. //
  1099. // Attempt to block until data free but don't stop
  1100. // the recovery if can not block the data
  1101. //
  1102. MsgDatabaseLock(MSG_GET_EXCLUSIVE,"NetrMessageNameDel");
  1103. i = MsgLookupName(neti,ncb.ncb_name);
  1104. if(i != -1)
  1105. {
  1106. SD_NAMEFLAGS(neti,i) &= ~NFDEL_PENDING;
  1107. if (g_IsTerminalServer)
  1108. {
  1109. MSG_LOG(TRACE,"NetrMessageNameDel: Unable to delete alias. Re-adding Session %x \n", SessionId);
  1110. // re-insert the session id in the list
  1111. MsgAddSessionInList(&(SD_SIDLIST(neti,i)), SessionId);
  1112. }
  1113. end_result = NERR_IncompleteDel; // Unable to delete name
  1114. }
  1115. else
  1116. {
  1117. //
  1118. // Another thread deleted this name while we were executing
  1119. // above, so this name no longer exists.
  1120. //
  1121. end_result = NERR_NotLocalName;
  1122. }
  1123. MsgDatabaseLock(MSG_RELEASE, "NetrMessageNameDel");
  1124. }
  1125. } // End for all nets
  1126. exit:
  1127. MsgConfigurationLock(MSG_RELEASE,"NetrMessageNameDel");
  1128. return(end_result);
  1129. }
  1130. DWORD
  1131. NetrSendMessage(
  1132. RPC_BINDING_HANDLE hRpcBinding,
  1133. LPSTR From,
  1134. LPSTR To,
  1135. LPSTR Text
  1136. )
  1137. /*++
  1138. Routine Description:
  1139. This is the RPC handler for the SendMessage RPC. It takes the arguments, transforms them, and
  1140. passes them to Msglogsbm for display.
  1141. Arguments:
  1142. None
  1143. Return Value:
  1144. None
  1145. --*/
  1146. {
  1147. DWORD length;
  1148. PCHAR newText;
  1149. DWORD dwMsgrState = GetMsgrState();
  1150. UNUSED(hRpcBinding);
  1151. if (dwMsgrState == STOPPING || dwMsgrState == STOPPED) {
  1152. return ERROR_SERVICE_NOT_ACTIVE;
  1153. }
  1154. MSG_LOG3(TRACE,
  1155. "NetrSendMessage, From '%s' To '%s' Text '%s'\n",
  1156. From, To, Text);
  1157. // Msglogsbm takes a wierd counted string argument with a short of length in the front.
  1158. // Whip one up
  1159. length = strlen( Text );
  1160. newText = LocalAlloc( LMEM_FIXED, length + 3 ); // newText should be aligned
  1161. if (newText == NULL)
  1162. {
  1163. return ERROR_NOT_ENOUGH_MEMORY;
  1164. }
  1165. *((PUSHORT) newText) = (USHORT) length;
  1166. strcpy( newText + 2, Text );
  1167. // Display the message
  1168. if (g_IsTerminalServer)
  1169. {
  1170. Msglogsbm( From, To, newText, (ULONG)EVERYBODY_SESSION_ID );
  1171. }
  1172. else
  1173. {
  1174. Msglogsbm (From, To, newText, 0);
  1175. }
  1176. LocalFree( newText );
  1177. return NO_ERROR;
  1178. }
  1179. NET_API_STATUS
  1180. MsgGetClientSessionId(
  1181. OUT PULONG pSessionId
  1182. )
  1183. /*++
  1184. Routine Description:
  1185. This function gets the session id of the client thread.
  1186. Note: it should be called only on HYDRA context, never in regular NT
  1187. Arguments:
  1188. pSessionId -
  1189. Return Value:
  1190. NET_API_STATUS - NERR_Success or reason for failure.
  1191. --*/
  1192. {
  1193. NET_API_STATUS status;
  1194. NTSTATUS ntstatus;
  1195. HANDLE CurrentThreadToken;
  1196. ULONG SessionId;
  1197. ULONG ReturnLength;
  1198. ntstatus = RpcImpersonateClient(NULL);
  1199. if (ntstatus != RPC_S_OK)
  1200. {
  1201. MSG_LOG1(ERROR,
  1202. "MsgGetClientSessionId: RpcImpersonateClient FAILED %#x\n",
  1203. ntstatus);
  1204. return NetpNtStatusToApiStatus(ntstatus);
  1205. }
  1206. ntstatus = NtOpenThreadToken(
  1207. NtCurrentThread(),
  1208. TOKEN_QUERY,
  1209. TRUE, // Use messenger service's security context to open thread token
  1210. &CurrentThreadToken
  1211. );
  1212. if (! NT_SUCCESS(ntstatus)) // error
  1213. {
  1214. MSG_LOG(ERROR,"MsgGetClientSessionId : Cannot open the current thread token %08lx\n", ntstatus);
  1215. }
  1216. else // OK
  1217. {
  1218. //
  1219. // Get the session id of the client thread
  1220. //
  1221. ntstatus = NtQueryInformationToken(
  1222. CurrentThreadToken,
  1223. TokenSessionId,
  1224. &SessionId,
  1225. sizeof(ULONG),
  1226. &ReturnLength);
  1227. if (! NT_SUCCESS(ntstatus)) // Error
  1228. {
  1229. MSG_LOG(ERROR,
  1230. "MsgGetClientSessionId: Cannot query current thread's token %08lx\n",
  1231. ntstatus);
  1232. NtClose(CurrentThreadToken);
  1233. }
  1234. else // OK
  1235. {
  1236. NtClose(CurrentThreadToken);
  1237. *pSessionId = SessionId;
  1238. }
  1239. }
  1240. RpcRevertToSelf();
  1241. status = NetpNtStatusToApiStatus(ntstatus);
  1242. //
  1243. // temporary security to avoid any problem:
  1244. // if we cannot get the session id,
  1245. // assume it is for the console.
  1246. //
  1247. if (status != NERR_Success)
  1248. {
  1249. *pSessionId = 0;
  1250. status = NERR_Success;
  1251. }
  1252. return status;
  1253. }