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.

845 lines
18 KiB

  1. /*++
  2. Copyright (c) 1993 Microsoft Corporation
  3. Module Name:
  4. vwipx.c
  5. Abstract:
  6. ntVdm netWare (Vw) IPX/SPX Functions
  7. Vw: The peoples' network
  8. Internal worker routines for DOS/WOW IPX calls (netware functions).
  9. The IPX APIs use WinSock to perform the actual operations
  10. Contents:
  11. _VwIPXCancelEvent
  12. _VwIPXCloseSocket
  13. _VwIPXGetInternetworkAddress
  14. _VwIPXGetIntervalMarker
  15. _VwIPXGetLocalTarget
  16. _VwIPXGetMaxPacketSize
  17. _VwIPXListenForPacket
  18. _VwIPXOpenSocket
  19. _VwIPXRelinquishControl
  20. _VwIPXScheduleIPXEvent
  21. _VwIPXSendPacket
  22. Author:
  23. Richard L Firth (rfirth) 30-Sep-1993
  24. Environment:
  25. User-mode Win32
  26. Revision History:
  27. 30-Sep-1993 rfirth
  28. Created
  29. --*/
  30. #include "vw.h"
  31. #pragma hdrstop
  32. extern WORD AesTickCount;
  33. //
  34. // functions
  35. //
  36. WORD
  37. _VwIPXCancelEvent(
  38. IN LPECB pEcb
  39. )
  40. /*++
  41. Routine Description:
  42. Internal routine shared by DOS and WIN that cancels event
  43. described by an ECB
  44. This call is Synchronous
  45. Arguments:
  46. Inputs
  47. pECB
  48. Return Value:
  49. 00h Success
  50. F9h Can't cancel ECB
  51. FFh ECB not in use
  52. --*/
  53. {
  54. LPXECB pXecb;
  55. WORD status;
  56. if (!pEcb) {
  57. return IPX_ECB_NOT_IN_USE;
  58. }
  59. //
  60. // if the ECB is still in the state we left it then LinkAddress will be the
  61. // address of the XECB which subsequently points back to the ECB. If both
  62. // these pan out then we have an ECB which we have at least seen before.
  63. // Maybe we can cancel it?
  64. //
  65. // Note: we grab the serialization semaphore here just in case the AES thread
  66. // is about to complete the ECB
  67. //
  68. status = IPX_CANNOT_CANCEL;
  69. RequestMutex();
  70. pXecb = (LPXECB)pEcb->LinkAddress;
  71. if (pXecb) {
  72. try {
  73. if (pXecb->Ecb == pEcb) {
  74. status = IPX_SUCCESS;
  75. //
  76. // pXecb ok: increase reference count in case other thread tries
  77. // to deallocate it while we're trying to cancel it
  78. //
  79. ++pXecb->RefCount;
  80. }
  81. } except(1) {
  82. //
  83. // bad pointer: bogus ECB
  84. //
  85. }
  86. } else {
  87. //
  88. // NULL pointer: event probably completed already
  89. //
  90. status = IPX_ECB_NOT_IN_USE;
  91. }
  92. ReleaseMutex();
  93. if (status == IPX_SUCCESS) {
  94. ECB_CANCEL_ROUTINE cancelRoutine;
  95. //
  96. // we have an ECB to cancel. If we still have it, it will be on one of
  97. // the socket queues, the timer list or the async completion list. If
  98. // the latter we are in a race. Treat such events as already happened.
  99. // We will cancel events on the timer list and queued send and receive
  100. // events only
  101. //
  102. switch (pXecb->QueueId) {
  103. case NO_QUEUE:
  104. status = ECB_CC_CANCELLED;
  105. goto cancel_exit;
  106. case ASYNC_COMPLETION_QUEUE:
  107. cancelRoutine = CancelAsyncEvent;
  108. break;
  109. case TIMER_QUEUE:
  110. cancelRoutine = CancelTimerEvent;
  111. break;
  112. case SOCKET_HEADER_QUEUE: //Multi-User Addition
  113. case SOCKET_LISTEN_QUEUE:
  114. case SOCKET_SEND_QUEUE:
  115. cancelRoutine = CancelSocketEvent;
  116. break;
  117. case CONNECTION_CONNECT_QUEUE:
  118. case CONNECTION_SEND_QUEUE:
  119. //
  120. // SPXEstablishConnection and SPXSendSequencedPacket cannot be
  121. // cancelled using IPXCancelEvent
  122. //
  123. status = ECB_CC_CANNOT_CANCEL;
  124. goto cancel_exit;
  125. case CONNECTION_ACCEPT_QUEUE:
  126. case CONNECTION_LISTEN_QUEUE:
  127. cancelRoutine = CancelConnectionEvent;
  128. break;
  129. }
  130. return cancelRoutine(pXecb);
  131. }
  132. //
  133. // app tried to sneak us an unknown ECB, -OR- the ECB was stomped on,
  134. // destroying the LinkAddress and hence the address of the XECB. We
  135. // could search the various lists looking for an XECB whose Ecb field
  136. // matches pEcb, but if the app has scribbled over the ECB when we
  137. // (make that Novell) told it not to, chances are it would fail real
  138. // well on DOS. Probable worst case is that the app is terminating and
  139. // the ECB may sometime later call an ESR which won't be there. Crashola
  140. //
  141. cancel_exit:
  142. IPXDBGPRINT((__FILE__, __LINE__,
  143. FUNCTION_IPXCancelEvent,
  144. IPXDBG_LEVEL_ERROR,
  145. "VwIPXCancelEvent: cannot find/cancel ECB %04x:%04x\n",
  146. HIWORD(pEcb),
  147. LOWORD(pEcb)
  148. ));
  149. pEcb->CompletionCode = (BYTE)status;
  150. pEcb->InUse = ECB_IU_NOT_IN_USE;
  151. return status;
  152. }
  153. VOID
  154. _VwIPXCloseSocket(
  155. IN WORD socketNumber
  156. )
  157. /*++
  158. Routine Description:
  159. Closes a socket and cancels any outstanding events on the socket.
  160. Closing an unopened socket does not return an error
  161. ESRs in cancelled ECBs are not called
  162. This call is Synchronous
  163. Arguments:
  164. Inputs
  165. socketNumber
  166. Return Value:
  167. None.
  168. --*/
  169. {
  170. LPSOCKET_INFO pSocketInfo;
  171. pSocketInfo = FindSocket(socketNumber);
  172. if (pSocketInfo != NULL) {
  173. KillSocket(pSocketInfo);
  174. } else {
  175. IPXDBGPRINT((__FILE__, __LINE__,
  176. FUNCTION_IPXCloseSocket,
  177. IPXDBG_LEVEL_WARNING,
  178. "_VwIPXCloseSocket: can't locate socket 0x%04x\n",
  179. B2LW(socketNumber)
  180. ));
  181. }
  182. }
  183. VOID
  184. _VwIPXGetInternetworkAddress(
  185. IN LPINTERNET_ADDRESS pNetworkAddress
  186. )
  187. /*++
  188. Routine Description:
  189. Returns a buffer containing the net number and node number for this
  190. station.
  191. This function cannot return an error (!)
  192. Assumes: 1. GetInternetAddress has been successfully called in the
  193. DLL initialization phase
  194. This call is Synchronous
  195. Arguments:
  196. Inputs
  197. Nothing.
  198. Outputs
  199. pNetworkAddress
  200. Return Value:
  201. None.
  202. --*/
  203. {
  204. CopyMemory((LPBYTE)pNetworkAddress,
  205. (LPBYTE)&MyInternetAddress.sa_netnum,
  206. sizeof(*pNetworkAddress)
  207. );
  208. IPXDBGPRINT((__FILE__, __LINE__,
  209. FUNCTION_IPXGetInternetworkAddress,
  210. IPXDBG_LEVEL_INFO,
  211. "VwIPXGetInternetworkAddress: %02x-%02x-%02x-%02x : %02x-%02x-%02x-%02x-%02x-%02x\n",
  212. pNetworkAddress->Net[0] & 0xff,
  213. pNetworkAddress->Net[1] & 0xff,
  214. pNetworkAddress->Net[2] & 0xff,
  215. pNetworkAddress->Net[3] & 0xff,
  216. pNetworkAddress->Node[0] & 0xff,
  217. pNetworkAddress->Node[1] & 0xff,
  218. pNetworkAddress->Node[2] & 0xff,
  219. pNetworkAddress->Node[3] & 0xff,
  220. pNetworkAddress->Node[4] & 0xff,
  221. pNetworkAddress->Node[5] & 0xff
  222. ));
  223. }
  224. WORD
  225. _VwIPXGetIntervalMarker(
  226. VOID
  227. )
  228. /*++
  229. Routine Description:
  230. Just returns the tick count maintained by Asynchronous Event Scheduler
  231. This call is Synchronous
  232. Arguments:
  233. None.
  234. Return Value:
  235. The tick count.
  236. --*/
  237. {
  238. // Sleep(0);
  239. Sleep(1); //Multi-User change
  240. return AesTickCount;
  241. }
  242. WORD
  243. _VwIPXGetLocalTarget(
  244. IN LPBYTE pNetworkAddress,
  245. OUT LPBYTE pImmediateAddress,
  246. OUT ULPWORD pTransportTime
  247. )
  248. /*++
  249. Routine Description:
  250. Given a target address of the form (network address {4}, node address {6}),
  251. returns the node address of the target if on the same network, or the node
  252. address of the router which knows how to get to the next hop in reaching the
  253. eventual target
  254. This call is Synchronous
  255. Arguments:
  256. Inputs
  257. pNetworkAddress
  258. Outputs
  259. pImmediateAddress
  260. pTransportTime
  261. Return Value:
  262. 00h Success
  263. F1h Ipx/Spx Not Initialized
  264. FAh No path to destination node found
  265. --*/
  266. {
  267. //
  268. // the transport handles real routing, so we always return the immediate
  269. // address as the target address. The transport will only look at the
  270. // target when routing
  271. //
  272. CopyMemory( pImmediateAddress,
  273. pNetworkAddress + 4,
  274. 6
  275. );
  276. *pTransportTime = 1; // ticks
  277. IPXDBGPRINT((__FILE__, __LINE__,
  278. FUNCTION_IPXGetLocalTarget,
  279. IPXDBG_LEVEL_INFO,
  280. "VwIPXGetLocalTarget: IN: %02x-%02x-%02x-%02x:%02x-%02x-%02x-%02x-%02x-%02x OUT: %02x-%02x-%02x-%02x-%02x-%02x\n",
  281. ((LPINTERNET_ADDRESS)pNetworkAddress)->Net[0] & 0xff,
  282. ((LPINTERNET_ADDRESS)pNetworkAddress)->Net[1] & 0xff,
  283. ((LPINTERNET_ADDRESS)pNetworkAddress)->Net[2] & 0xff,
  284. ((LPINTERNET_ADDRESS)pNetworkAddress)->Net[3] & 0xff,
  285. ((LPINTERNET_ADDRESS)pNetworkAddress)->Node[0] & 0xff,
  286. ((LPINTERNET_ADDRESS)pNetworkAddress)->Node[1] & 0xff,
  287. ((LPINTERNET_ADDRESS)pNetworkAddress)->Node[2] & 0xff,
  288. ((LPINTERNET_ADDRESS)pNetworkAddress)->Node[3] & 0xff,
  289. ((LPINTERNET_ADDRESS)pNetworkAddress)->Node[4] & 0xff,
  290. ((LPINTERNET_ADDRESS)pNetworkAddress)->Node[5] & 0xff,
  291. pImmediateAddress[0] & 0xff,
  292. pImmediateAddress[1] & 0xff,
  293. pImmediateAddress[2] & 0xff,
  294. pImmediateAddress[3] & 0xff,
  295. pImmediateAddress[4] & 0xff,
  296. pImmediateAddress[5] & 0xff
  297. ));
  298. return IPX_SUCCESS;
  299. }
  300. WORD
  301. _VwIPXGetMaxPacketSize(
  302. OUT ULPWORD pRetryCount
  303. )
  304. /*++
  305. Routine Description:
  306. Returns the maximum packet size the underlying network can handle
  307. Assumes: 1. A successfull call to GetMaxPacketSize has been made during
  308. DLL initialization
  309. 2. Maximum packet size is constant
  310. This call is Synchronous
  311. Arguments:
  312. Outputs
  313. pRetryCount
  314. Return Value:
  315. The maximum packet size.
  316. --*/
  317. {
  318. if ( pRetryCount ) {
  319. *pRetryCount = 5; // arbitrary?
  320. }
  321. return MyMaxPacketSize;
  322. }
  323. WORD
  324. _VwIPXListenForPacket(
  325. IN LPECB pEcb,
  326. IN ECB_ADDRESS EcbAddress
  327. )
  328. /*++
  329. Routine Description:
  330. Queue a listen request against a socket. All listen requests will be
  331. completed asynchronously, unless cancelled by app
  332. This call is Asynchronous
  333. Arguments:
  334. Inputs
  335. pEcb
  336. EcbAddress
  337. Return Value:
  338. None.
  339. --*/
  340. {
  341. LPXECB pXecb = RetrieveXEcb(ECB_TYPE_IPX, pEcb, EcbAddress);
  342. LPSOCKET_INFO pSocketInfo;
  343. IPXDBGPRINT((__FILE__, __LINE__,
  344. FUNCTION_IPXListenForPacket,
  345. IPXDBG_LEVEL_INFO,
  346. "_VwIPXListenForPacket(%04x:%04x) socket=%04x ESR=%04x:%04x\n",
  347. HIWORD(EcbAddress),
  348. LOWORD(EcbAddress),
  349. B2LW(pXecb->SocketNumber),
  350. HIWORD(pXecb->EsrAddress),
  351. LOWORD(pXecb->EsrAddress)
  352. ));
  353. //
  354. // don't know what real IPX/SPX does if it gets a NULL pointer
  355. //
  356. if (!pXecb) {
  357. return IPX_BAD_REQUEST;
  358. }
  359. //
  360. // the socket must be open already before we can perform a listen
  361. //
  362. pSocketInfo = FindSocket(pXecb->SocketNumber);
  363. //
  364. // we also return NON_EXISTENT_SOCKET (0xFF) if the socket is in use for SPX
  365. //
  366. //
  367. // There is nothing in the netware documentation that explains
  368. // what gets returned if this is the case, only a warning about IPX listens
  369. // and sends can't be made on a socket open for SPX. Really definitive
  370. //
  371. if (!pSocketInfo || pSocketInfo->SpxSocket) {
  372. CompleteEcb(pXecb, ECB_CC_NON_EXISTENT_SOCKET);
  373. return IPX_NON_EXISTENT_SOCKET;
  374. }
  375. //
  376. // initiate the receive. It may complete if there is data waiting or an
  377. // error occurs, otherwise the ECB will be placed in a receive pending queue
  378. // for this socket
  379. //
  380. if (GetIoBuffer(pXecb, FALSE, IPX_HEADER_LENGTH)) {
  381. pXecb->Ecb->InUse = ECB_IU_LISTENING;
  382. IpxReceiveFirst(pXecb, pSocketInfo);
  383. } else {
  384. CompleteEcb(pXecb, ECB_CC_CANCELLED);
  385. }
  386. //
  387. // return success. Any errors will be communicated asynchronously - either
  388. // indirectly by relevant values in CompletionCode and InUse fields of the
  389. // ECB or directly by an ESR callback
  390. //
  391. return IPX_SUCCESS;
  392. }
  393. WORD
  394. _VwIPXOpenSocket(
  395. IN OUT ULPWORD pSocketNumber,
  396. IN BYTE socketType,
  397. IN WORD dosPDB
  398. )
  399. /*++
  400. Routine Description:
  401. Opens a socket for use by IPX or SPX. Puts the socket into non-blocking mode.
  402. The socket will be bound to IPX
  403. This call is Synchronous
  404. Arguments:
  405. Inputs
  406. *pSocketNumber - The requested socket number
  407. socketType - Socket Longevity flag
  408. dosPDB - DOS PDB. This parameter is not part of the IPX API.
  409. Added because we need to remember which DOS executable created
  410. the socket: we need to clean-up short-lived sockets when the
  411. executable terminates
  412. Outputs
  413. pSocketNumber - Assigned socket number
  414. Return Value:
  415. 00h Success
  416. F0h Ipx Not Installed
  417. F1h Ipx/Spx Not Initialized
  418. FEh Socket table full
  419. FFh Socket already open
  420. --*/
  421. {
  422. LPSOCKET_INFO pSocketInfo;
  423. WORD status;
  424. if ((pSocketInfo = AllocateSocket()) == NULL) {
  425. return IPX_SOCKET_TABLE_FULL;
  426. }
  427. status = (WORD) CreateSocket(SOCKET_TYPE_IPX, pSocketNumber, &pSocketInfo->Socket);
  428. if (status == IPX_SUCCESS) {
  429. //
  430. // set up the SOCKET_INFO fields and add it to our list of open sockets
  431. //
  432. pSocketInfo->Owner = dosPDB;
  433. pSocketInfo->SocketNumber = *pSocketNumber;
  434. //
  435. // treat socketType == 0 as short-lived, anything else as long-lived.
  436. // There doesn't appear to be an error return if the flag is not 0 or 0xFF
  437. //
  438. pSocketInfo->LongLived = (BOOL)(socketType != 0);
  439. QueueSocket(pSocketInfo);
  440. IPXDBGPRINT((__FILE__, __LINE__,
  441. FUNCTION_IPXOpenSocket,
  442. IPXDBG_LEVEL_INFO,
  443. "_VwIPXOpenSocket: created socket %04x\n",
  444. B2LW(*pSocketNumber)
  445. ));
  446. } else {
  447. DeallocateSocket(pSocketInfo);
  448. IPXDBGPRINT((__FILE__, __LINE__,
  449. FUNCTION_IPXOpenSocket,
  450. IPXDBG_LEVEL_ERROR,
  451. "_VwIPXOpenSocket: Failure: returning %x\n",
  452. status
  453. ));
  454. }
  455. return status;
  456. }
  457. VOID
  458. _VwIPXRelinquishControl(
  459. VOID
  460. )
  461. /*++
  462. Routine Description:
  463. Just sleep for a nominal amount.
  464. This call is Synchronous
  465. Arguments:
  466. None.
  467. Return Value:
  468. None.
  469. --*/
  470. {
  471. Sleep(0);
  472. }
  473. VOID
  474. _VwIPXScheduleIPXEvent(
  475. IN WORD time,
  476. IN LPECB pEcb,
  477. IN ECB_ADDRESS EcbAddress
  478. )
  479. /*++
  480. Routine Description:
  481. Schedules a an event to occur in some number of ticks. When the tick count
  482. reaches 0, the ECB InUse field is cleared and any ESR called
  483. This call is Asynchronous
  484. Arguments:
  485. Inputs
  486. time - the delay time ( number of 1/18 second ticks )
  487. pEcb
  488. EcbAddress
  489. Outputs
  490. Nothing
  491. Return Value:
  492. None.
  493. --*/
  494. {
  495. LPXECB pXecb = RetrieveXEcb(ECB_TYPE_IPX, pEcb, EcbAddress);
  496. // tommye - MS 30525
  497. //
  498. // Make sure the xecb alloc didn't fail
  499. //
  500. if (pXecb == NULL) {
  501. return;
  502. }
  503. ScheduleEvent(pXecb, time);
  504. }
  505. VOID
  506. _VwIPXSendPacket(
  507. IN LPECB pEcb,
  508. IN ECB_ADDRESS EcbAddress,
  509. IN WORD DosPDB
  510. )
  511. /*++
  512. Routine Description:
  513. Sends a packet to the target machine/router. This call can be made on a
  514. socket that is not open
  515. The app must have filled in the following IPX_ECB fields:
  516. EsrAddress
  517. Socket
  518. ImmediateAddress
  519. FragmentCount
  520. fragment descriptor fields
  521. and the following IPX_PACKET fields:
  522. PacketType
  523. Destination.Net
  524. Destination.Node
  525. Destination.Socket
  526. This call is Asynchronous
  527. Arguments:
  528. Inputs
  529. pEcb
  530. EcbAddress
  531. DosPDB
  532. Return Value:
  533. None.
  534. --*/
  535. {
  536. LPXECB pXecb = RetrieveXEcb(ECB_TYPE_IPX, pEcb, EcbAddress);
  537. LPSOCKET_INFO pSocketInfo;
  538. // tommye - MS 30525
  539. //
  540. // Make sure the XEcb alloc didn't fail
  541. //
  542. if (pXecb == NULL) {
  543. return;
  544. }
  545. //
  546. // this function returns no immediate status so we must assume that the
  547. // ECB pointer is valid
  548. //
  549. //
  550. // check the ECB for correctness
  551. //
  552. if ((pXecb->Ecb->FragmentCount == 0)
  553. || (ECB_FRAGMENT(pXecb->Ecb, 0)->Length < IPX_HEADER_LENGTH)) {
  554. CompleteEcb(pXecb, ECB_CC_BAD_REQUEST);
  555. return;
  556. }
  557. //
  558. // IPXSendPacket() can be called on an unopened socket: we must try to
  559. // temporarily allocate the socket
  560. //
  561. // Q: Is the following scenario possible with real IPX:
  562. // IPXSendPacket() on unopened socket X
  563. // Send fails & gets queued
  564. // app makes IPXOpenSocket() call on X; X gets opened
  565. //
  566. // Currently, we would create the temporary socket and fail IPXOpenSocket()
  567. // because it is already open!
  568. //
  569. pSocketInfo = FindSocket(pXecb->SocketNumber);
  570. if (!pSocketInfo) {
  571. //
  572. // when is temporary socket deleted? After send completed?
  573. // when app dies? when? Novell documentation is not specific (used
  574. // to say something else :-))
  575. //
  576. pSocketInfo = AllocateTemporarySocket();
  577. if (pSocketInfo) {
  578. //
  579. // set up the SOCKET_INFO fields and add it to our list of open sockets
  580. //
  581. pSocketInfo->Owner = DosPDB;
  582. //
  583. // temporary sockets are always short-lived
  584. //
  585. pSocketInfo->LongLived = FALSE;
  586. QueueSocket(pSocketInfo);
  587. } else {
  588. CompleteEcb(pXecb, ECB_CC_SOCKET_TABLE_FULL);
  589. return;
  590. }
  591. } else if (pSocketInfo->SpxSocket) {
  592. //
  593. // see complaint in IPXListenForPacket
  594. //
  595. // can't make IPX requests on socket opened for SPX
  596. //
  597. CompleteEcb(pXecb, ECB_CC_NON_EXISTENT_SOCKET);
  598. return;
  599. }
  600. //
  601. // start the send: tries to send the data in one go. Either succeeds, fails
  602. // with an error, or queues the ECB for subsequent attempts via AES/IPX
  603. // deferred processing.
  604. //
  605. // In the first 2 cases, the ECB has been completed already
  606. //
  607. StartIpxSend(pXecb, pSocketInfo);
  608. }