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.

927 lines
24 KiB

  1. /*==========================================================================;
  2. *
  3. * Copyright (C) 1994-1997 Microsoft Corporation. All Rights Reserved.
  4. *
  5. * File: wsock2.c
  6. * Content: DirectPlay Winsock 2 SP support. Called from dpsp.c.
  7. * History:
  8. * Date By Reason
  9. * ==== == ======
  10. * 7/11//97 andyco created it
  11. * 2/13/98 aarono added async support.
  12. * 4/6/98 aarono mapped WSAECONNRESET to DPERR_CONNECTIONLOST
  13. * 6/6/98 aarono B#27187 fix ref counting on send blocks in sync error case
  14. * 7/9/99 aarono Cleaning up GetLastError misuse, must call right away,
  15. * before calling anything else, including DPF.
  16. **************************************************************************/
  17. // this module is for async connections and sends
  18. // only used w/ TCP:IP - IPX is dgram only, so we don't bother...
  19. // currently only used as the reply thread proc for async replies. see dpsp.c::sp_reply
  20. #define INCL_WINSOCK_API_TYPEDEFS 1 // includes winsock 2 fn proto's, for getprocaddress
  21. #include <winsock2.h>
  22. #include "dpsp.h"
  23. #undef DPF_MODNAME
  24. #define DPF_MODNAME "AsyncSendThreadProc"
  25. extern HINSTANCE hWS2; // dynaload the ws2_32.dll, so if it's not installed
  26. // (e.g. win 95 gold) we still load
  27. // prototypes for our dynaload fn's
  28. LPFN_WSAWAITFORMULTIPLEEVENTS g_WSAWaitForMultipleEvents;
  29. LPFN_WSASEND g_WSASend;
  30. LPFN_WSASENDTO g_WSASendTo;
  31. LPFN_WSACLOSEEVENT g_WSACloseEvent;
  32. LPFN_WSACREATEEVENT g_WSACreateEvent;
  33. LPFN_WSAENUMNETWORKEVENTS g_WSAEnumNetworkEvents;
  34. LPFN_WSAEVENTSELECT g_WSAEventSelect;
  35. LPFN_GETSOCKOPT g_getsockopt;
  36. // attempt to load the winsock 2 dll, and get our proc addresses from it
  37. HRESULT InitWinsock2()
  38. {
  39. // load winsock library
  40. hWS2 = LoadLibrary("ws2_32.dll");
  41. if (!hWS2)
  42. {
  43. DPF(0,"Could not load ws2_32.dll\n");
  44. // reset our winsock 2 global
  45. goto LOADLIBRARYFAILED;
  46. }
  47. // get pointers to the entry points we need
  48. g_WSAWaitForMultipleEvents = (LPFN_WSAWAITFORMULTIPLEEVENTS)GetProcAddress(hWS2, "WSAWaitForMultipleEvents");
  49. if(!g_WSAWaitForMultipleEvents) goto GETPROCADDRESSFAILED;
  50. g_WSASend = (LPFN_WSASEND)GetProcAddress(hWS2, "WSASend");
  51. if (!g_WSASend) goto GETPROCADDRESSFAILED;
  52. g_WSASendTo = (LPFN_WSASENDTO)GetProcAddress(hWS2, "WSASendTo");
  53. if (!g_WSASendTo) goto GETPROCADDRESSFAILED;
  54. g_WSAEventSelect = ( LPFN_WSAEVENTSELECT )GetProcAddress(hWS2, "WSAEventSelect");
  55. if (!g_WSAEventSelect) goto GETPROCADDRESSFAILED;
  56. g_WSAEnumNetworkEvents = (LPFN_WSAENUMNETWORKEVENTS)GetProcAddress(hWS2, "WSAEnumNetworkEvents");
  57. if (!g_WSAEnumNetworkEvents) goto GETPROCADDRESSFAILED;
  58. g_WSACreateEvent = (LPFN_WSACREATEEVENT)GetProcAddress(hWS2, "WSACreateEvent");
  59. if (!g_WSACreateEvent) goto GETPROCADDRESSFAILED;
  60. g_WSACloseEvent = (LPFN_WSACLOSEEVENT)GetProcAddress(hWS2, "WSACloseEvent");
  61. if (!g_WSACloseEvent) goto GETPROCADDRESSFAILED;
  62. g_getsockopt = (LPFN_GETSOCKOPT)GetProcAddress(hWS2, "getsockopt");
  63. if (!g_getsockopt) goto GETPROCADDRESSFAILED;
  64. return DP_OK;
  65. GETPROCADDRESSFAILED:
  66. DPF(0,"Could not find required Winsock entry point");
  67. FreeLibrary(hWS2);
  68. hWS2 = NULL;
  69. // fall through
  70. LOADLIBRARYFAILED:
  71. g_WSAEventSelect = NULL;
  72. g_WSAEnumNetworkEvents = NULL;
  73. g_WSACreateEvent = NULL;
  74. g_WSACloseEvent = NULL;
  75. return DPERR_UNAVAILABLE;
  76. } // InitWinsock2
  77. // remove the reply node from the list
  78. void DeleteReplyNode(LPGLOBALDATA pgd,LPREPLYLIST prd, BOOL bKillSocket)
  79. {
  80. LPREPLYLIST prdPrev;
  81. ENTER_DPSP();
  82. // 1st, remove prd from the list
  83. // is it the root?
  84. if (prd == pgd->pReplyList) pgd->pReplyList = pgd->pReplyList->pNextReply;
  85. else
  86. {
  87. BOOL bFound = FALSE;
  88. // it's not the root - take it out of the middle
  89. prdPrev = pgd->pReplyList;
  90. while (prdPrev && !bFound)
  91. {
  92. if (prdPrev->pNextReply == prd)
  93. {
  94. prdPrev->pNextReply = prd->pNextReply;
  95. bFound = TRUE;
  96. }
  97. else
  98. {
  99. prdPrev = prdPrev->pNextReply;
  100. }
  101. } // while
  102. ASSERT(bFound);
  103. } // not the root
  104. // now clean up prd
  105. // nuke the socket
  106. if (bKillSocket)
  107. KillSocket(prd->sSocket,TRUE,FALSE);
  108. // free up the node
  109. if (prd->lpMessage) MemFree(prd->lpMessage);
  110. MemFree(prd);
  111. LEAVE_DPSP();
  112. return ;
  113. } // DeleteReplyNode
  114. /*
  115. ** AsyncConnectAndSend
  116. *
  117. * CALLED BY: AsyncSendThreadProc
  118. *
  119. * DESCRIPTION:
  120. *
  121. * if necessary, creates a non-blocking socket, and initiates a connection
  122. * to address specified in prd
  123. * once connection has been completed, does a synchronous (blocking) send and
  124. * removes prd from the global list
  125. */
  126. HRESULT AsyncConnectAndSend(LPGLOBALDATA pgd,LPREPLYLIST prd)
  127. {
  128. UINT err;
  129. HRESULT hr;
  130. UINT addrlen = sizeof(SOCKADDR);
  131. BOOL bConnectionExists = FALSE;
  132. BOOL bKillConnection = TRUE;
  133. if (INVALID_SOCKET == prd->sSocket)
  134. {
  135. u_long lNonBlock = 1; // passed to ioctlsocket to make socket non-blocking
  136. DPID dpidPlayer=0;
  137. #ifdef FULLDUPLEX_SUPPORT
  138. // if client wants us to reuse a connection, it would have indicated so and the connection
  139. // would have been added to our send list by now. See if it exists.
  140. // TODO - we don't want to search the list everytime - find a better way
  141. bConnectionExists = FindSocketInBag(pgd, &prd->sockaddr, &prd->sSocket, &dpidPlayer);
  142. #endif // FULLDUPLEX_SUPPORT
  143. if (!bConnectionExists)
  144. {
  145. SOCKET sSocket;
  146. // socket didn't exist in our send list, let's send it on a new temporary connection
  147. DEBUGPRINTADDR(9,"Sending aync reply on a new connection to - ", &(prd->sockaddr));
  148. // need to get the new socket
  149. hr = CreateSocket(pgd,&sSocket,SOCK_STREAM,0,INADDR_ANY,&err,FALSE);
  150. if (FAILED(hr))
  151. {
  152. DPF(0,"create async socket failed - err = %d\n",err);
  153. return hr;
  154. }
  155. prd->sSocket = sSocket;
  156. // set socket to non-blocking
  157. err = ioctlsocket(prd->sSocket,FIONBIO,&lNonBlock);
  158. if (SOCKET_ERROR == err)
  159. {
  160. err = WSAGetLastError();
  161. DPF(0,"could not set non-blocking mode on socket err = %d!",err);
  162. DPF(0,"will revert to synchronous behavior. bummer");
  163. }
  164. // now, start the connect
  165. SetReturnAddress(prd->lpMessage,pgd->sSystemStreamSocket);
  166. err = connect(prd->sSocket,&prd->sockaddr,addrlen);
  167. if (SOCKET_ERROR == err)
  168. {
  169. err = WSAGetLastError();
  170. if (WSAEWOULDBLOCK == err)
  171. {
  172. // this is expected. the operation needs time to complete.
  173. // select will tell us when the socket is good to go.
  174. return DP_OK;
  175. }
  176. // else it's a real error!
  177. DPF(0,"async reply - connect failed - error = %d\n",err);
  178. DEBUGPRINTADDR(0,"async reply - connect failed - addr = ",(LPSOCKADDR)&(prd->sockaddr));
  179. goto CLEANUP_EXIT;
  180. }
  181. }
  182. else
  183. {
  184. // we found our connection, let's reuse it
  185. // set it to non-blocking
  186. DEBUGPRINTADDR(9,"Sending async reply on an existing connection to - ", &(prd->sockaddr));
  187. err = ioctlsocket(prd->sSocket,FIONBIO,&lNonBlock);
  188. if (SOCKET_ERROR == err)
  189. {
  190. err = WSAGetLastError();
  191. DPF(0,"could not set non-blocking mode on socket err = %d!",err);
  192. DPF(0,"will revert to synchronous behavior. bummer");
  193. }
  194. // once we have a player id, the session has started. let's hold on to the connection
  195. // we have and reuse it for the rest of the session
  196. if (dpidPlayer) bKillConnection = FALSE;
  197. } // FindSocketInBag
  198. } // INVALID_SOCKET
  199. // once we get here, we should have a connected socket ready to send!
  200. err = 0;
  201. // keep spitting bits at the socket until we finish or get an error
  202. while ((prd->dwBytesLeft != 0) && (SOCKET_ERROR != err))
  203. {
  204. err = send(prd->sSocket,prd->pbSend,prd->dwBytesLeft,0);
  205. if (SOCKET_ERROR != err)
  206. {
  207. // some bytes went out on the wire
  208. prd->dwBytesLeft -= err; // we just sent err bytes
  209. prd->pbSend += err; // advance our send buffer by err bytes
  210. }
  211. }
  212. // now, we've either finished the send, or we have an error
  213. if (SOCKET_ERROR == err)
  214. {
  215. err = WSAGetLastError();
  216. if (WSAEWOULDBLOCK == err)
  217. {
  218. // this means we couldn't send any bytes w/o blocking
  219. // that's ok. we'll let select tell us when it's ready to not block
  220. return DP_OK;
  221. }
  222. // else it's a real eror!
  223. // any other error, we give up and clean up this reply
  224. DPF(0,"async send - send failed - error = %d\n",err);
  225. DEBUGPRINTADDR(0,"async send - send failed - addr = ",(LPSOCKADDR)&(prd->sockaddr));
  226. }
  227. else ASSERT(0 == prd->dwBytesLeft); // if it's not an error, we better have sent it all
  228. // fall through
  229. CLEANUP_EXIT:
  230. if (bConnectionExists && bKillConnection)
  231. {
  232. // close the connection after we're done
  233. RemoveSocketFromReceiveList(pgd,prd->sSocket);
  234. RemoveSocketFromBag(pgd,prd->sSocket);
  235. // so DeleteReplyNode won't try to kill socket again
  236. prd->sSocket = INVALID_SOCKET;
  237. }
  238. // remove the node from the list
  239. DeleteReplyNode(pgd,prd,bKillConnection);
  240. return DP_OK;
  241. } // AsyncConnectAndSend
  242. // walk the reply list, tell winsock to watch any of the nodes which has a valid socket
  243. // (i.e. has a connection or send pending)
  244. HRESULT DoEventSelect(LPGLOBALDATA pgd,WSAEVENT hSelectEvent)
  245. {
  246. UINT err;
  247. LPREPLYLIST prd;
  248. ENTER_DPSP();
  249. prd = pgd->pReplyList;
  250. while (prd)
  251. {
  252. if (INVALID_SOCKET != prd->sSocket)
  253. {
  254. // have winscok tell us when anything good (connection complete, ready to write more data)
  255. // happens on this socket
  256. err = g_WSAEventSelect(prd->sSocket,hSelectEvent,FD_WRITE | FD_CONNECT);
  257. if (SOCKET_ERROR == err)
  258. {
  259. err = WSAGetLastError();
  260. DPF(0,"could not do event select ! err = %d!",err);
  261. // keep trying...
  262. }
  263. } // invalid_socket
  264. prd = prd->pNextReply;
  265. }
  266. LEAVE_DPSP();
  267. return DP_OK;
  268. } // DoEventSelect
  269. // wsaeventselect woke us up. one or more of our sockets had something happen
  270. // (e.g. connect completed, send ready for more data, etc.)
  271. // walk the reply list, find nodes who need to be serviced
  272. void ServiceReplyList(LPGLOBALDATA pgd,WSAEVENT hEvent)
  273. {
  274. UINT err;
  275. LPREPLYLIST prd,prdNext;
  276. WSANETWORKEVENTS WSANetEvents;
  277. ENTER_DPSP();
  278. Top:
  279. prd = pgd->pReplyList;
  280. while (prd)
  281. {
  282. // save this now - asyncconnectandsend could destroy prd
  283. prdNext = prd->pNextReply;
  284. if (INVALID_SOCKET != prd->sSocket)
  285. {
  286. // go ask winsock if this socket had anything intersting happen
  287. err = g_WSAEnumNetworkEvents(prd->sSocket,NULL,&WSANetEvents);
  288. if (SOCKET_ERROR == err)
  289. {
  290. err = WSAGetLastError();
  291. DPF(0,"could not enum events!! err = %d!",err);
  292. // keep trying...
  293. }
  294. else
  295. {
  296. BOOL bError=FALSE;
  297. // no error - go see what we got
  298. if ((WSANetEvents.lNetworkEvents & FD_CONNECT) || (WSANetEvents.lNetworkEvents & FD_WRITE))
  299. {
  300. // was there an error?
  301. if (WSANetEvents.iErrorCode[FD_CONNECT_BIT])
  302. {
  303. // we got a connect error!
  304. DPF(0,"async reply - WSANetEvents - connect failed - error = %d\n",
  305. WSANetEvents.iErrorCode[FD_CONNECT_BIT]);
  306. DEBUGPRINTADDR(0,"async reply - connect failed - addr = ",
  307. (LPSOCKADDR)&(prd->sockaddr));
  308. RemovePendingAsyncSends(pgd, prd->dwPlayerTo);
  309. goto Top;
  310. }
  311. if (WSANetEvents.iErrorCode[FD_WRITE_BIT])
  312. {
  313. // we got a send error!
  314. DPF(0,"async reply - WSANetEvents - send failed - error = %d\n",
  315. WSANetEvents.iErrorCode[FD_WRITE_BIT]);
  316. DEBUGPRINTADDR(0,"async reply - send failed - addr = ",
  317. (LPSOCKADDR)&(prd->sockaddr));
  318. RemovePendingAsyncSends(pgd, prd->dwPlayerTo);
  319. goto Top;
  320. }
  321. // note - we try + send even if there was an error. seems like it's worth a shot...
  322. // go try + send
  323. AsyncConnectAndSend(pgd,prd);
  324. }
  325. }
  326. } // invalid_socket
  327. else
  328. {
  329. // it it's an invalid socket, we need to init our connect and send
  330. AsyncConnectAndSend(pgd,prd);
  331. }
  332. prd = prdNext;
  333. }
  334. LEAVE_DPSP();
  335. return ;
  336. } // ServiceReplyList
  337. // this thread works on doing async sends
  338. DWORD WINAPI AsyncSendThreadProc(LPVOID pvCast)
  339. {
  340. HRESULT hr=DP_OK;
  341. LPGLOBALDATA pgd = (LPGLOBALDATA) pvCast;
  342. HANDLE hHandleList[3];
  343. DWORD rc;
  344. WSAEVENT hSelectEvent; // event used by WSASelectEvent
  345. DPF(9,"Entered AsyncSendThreadProc\n");
  346. // get the event 4 selectevent
  347. hSelectEvent = g_WSACreateEvent();
  348. if (WSA_INVALID_EVENT == hSelectEvent)
  349. {
  350. rc = WSAGetLastError();
  351. DPF(0,"could not create winsock event - rc = %d\n",rc);
  352. ExitThread(0);
  353. return 0;
  354. }
  355. hHandleList[0] = hSelectEvent;
  356. hHandleList[1] = pgd->hReplyEvent;
  357. // This extra handle is here because of a Windows 95 bug. Windows
  358. // will occasionally miss when it walks the handle table, causing
  359. // my thread to wait on the wrong handles. By putting a guaranteed
  360. // invalid handle at the end of our array, the kernel will do a
  361. // forced re-walk of the handle table and find the correct handles.
  362. hHandleList[2] = INVALID_HANDLE_VALUE;
  363. while (1)
  364. {
  365. // tell winsock to watch all of our reply nodes. it will set our event
  366. // when something cool happens...
  367. DoEventSelect(pgd,hSelectEvent);
  368. // wait on our event. when it's set, we either split, or empty the reply list
  369. rc = WaitForMultipleObjectsEx(2,hHandleList,FALSE,INFINITE,TRUE);
  370. if ((DWORD)-1 == rc)
  371. {
  372. DWORD dwError = GetLastError();
  373. // rut roh! errror on the wait
  374. DPF(0,"!!!!! error on WaitForMultipleObjects -- async reply bailing -- dwError = %d",dwError);
  375. goto CLEANUP_EXIT;
  376. }
  377. if (rc == WAIT_OBJECT_0) // a-josbor: need to reset this manual event
  378. {
  379. ResetEvent(hSelectEvent);
  380. }
  381. // ok. someone woke us up. it could be 1. shutdown, or 2. one
  382. // of our sockets needs attention (i.e. a connect completed), or 3. someone
  383. // put a new reply node on the list
  384. // shutdown?
  385. if (pgd->bShutdown)
  386. {
  387. goto CLEANUP_EXIT;
  388. }
  389. DPF(8,"In AsyncSendThreadProc, servicing event %d\n", rc - WAIT_OBJECT_0);
  390. // otherwise, it must be a socket in need or a new replynode
  391. ServiceReplyList(pgd,hSelectEvent);
  392. } // 1
  393. CLEANUP_EXIT:
  394. ENTER_DPSP();
  395. // cleanout reply list
  396. while (pgd->pReplyList) DeleteReplyNode(pgd,pgd->pReplyList,TRUE);
  397. CloseHandle(pgd->hReplyEvent);
  398. pgd->hReplyEvent = 0;
  399. LEAVE_DPSP();
  400. g_WSACloseEvent(hSelectEvent);
  401. DPF(6,"replythreadproc exit");
  402. return 0;
  403. } // AsyncSendThreadProc
  404. HRESULT GetMaxUdpBufferSize(SOCKET socket, UINT * piMaxUdpDg)
  405. {
  406. INT iBufferSize;
  407. INT err;
  408. ASSERT(piMaxUdpDg);
  409. iBufferSize = sizeof(UINT);
  410. err = g_getsockopt(socket, SOL_SOCKET, SO_MAX_MSG_SIZE, (LPBYTE)piMaxUdpDg, &iBufferSize);
  411. if (SOCKET_ERROR == err)
  412. {
  413. DPF(0,"getsockopt for SO_MAX_MSG_SIZE returned err = %d", WSAGetLastError());
  414. return DPERR_UNAVAILABLE;
  415. }
  416. return DP_OK;
  417. }
  418. #ifdef SENDEX
  419. DWORD wsaoDecRef(LPSENDINFO pSendInfo)
  420. {
  421. #define pgd (pSendInfo->pgd)
  422. DWORD count;
  423. #ifdef DEBUG
  424. EnterCriticalSection(&pgd->csSendEx);
  425. count=(--pSendInfo->RefCount);
  426. LeaveCriticalSection(&pgd->csSendEx);
  427. #else
  428. count=InterlockedDecrement(&pSendInfo->RefCount);
  429. #endif
  430. if(!count){
  431. EnterCriticalSection(&pgd->csSendEx);
  432. Delete(&pSendInfo->PendingSendQ);
  433. pgd->dwBytesPending -= pSendInfo->dwMessageSize;
  434. pgd->dwMessagesPending -= 1;
  435. LeaveCriticalSection(&pgd->csSendEx);
  436. DPF(9,"RefCount 0 pSendInfo %x , SC context %x, status=%x \n",pSendInfo, pSendInfo->dwUserContext,pSendInfo->Status);
  437. if(pSendInfo->dwSendFlags & DPSEND_ASYNC){
  438. pSendInfo->lpISP->lpVtbl->SendComplete(pSendInfo->lpISP,(LPVOID)pSendInfo->dwUserContext,pSendInfo->Status);
  439. }
  440. pgd->pSendInfoPool->Release(pgd->pSendInfoPool, pSendInfo);
  441. } else {
  442. DPF(9,"wsaoDecRef pSendInfo %x, Refcount= %d\n",pSendInfo,pSendInfo->RefCount);
  443. }
  444. if(count& 0x80000000){
  445. DEBUG_BREAK();
  446. }
  447. return count;
  448. #undef pgd
  449. }
  450. void CALLBACK SendComplete(
  451. DWORD dwError,
  452. DWORD cbTransferred,
  453. LPWSAOVERLAPPED lpOverlapped,
  454. DWORD dwFlags
  455. )
  456. {
  457. LPSENDINFO lpSendInfo=(LPSENDINFO)CONTAINING_RECORD(lpOverlapped,SENDINFO,wsao);
  458. DPF(9,"DPWSOCK:SendComplete, lpSendInfo %x\n",lpSendInfo);
  459. if(dwError){
  460. DPF(0,"DPWSOCK: send completion error, dwError=x%x\n",dwError);
  461. lpSendInfo->Status=DPERR_GENERIC;
  462. }
  463. wsaoDecRef(lpSendInfo);
  464. }
  465. HRESULT DoSend(LPGLOBALDATA pgd, LPSENDINFO pSendInfo)
  466. {
  467. #define fAsync (pSendInfo->dwSendFlags & DPSEND_ASYNC)
  468. DWORD dwBytesSent;
  469. UINT err;
  470. HRESULT hr;
  471. if(pSendInfo->dwFlags & SI_RELIABLE){
  472. // Reliable Send
  473. DPF(9,"WSASend, pSendInfo %x\n",pSendInfo);
  474. // send the message
  475. err = g_WSASend(pSendInfo->sSocket,
  476. (LPWSABUF)&pSendInfo->SendArray[pSendInfo->iFirstBuf],
  477. pSendInfo->cBuffers,
  478. &dwBytesSent,
  479. 0, /*flags*/
  480. (fAsync)?(&pSendInfo->wsao):NULL,
  481. (fAsync)?(SendComplete):NULL);
  482. if(!err){
  483. DPF(9,"WSASend, sent synchronously, pSendInfo %x\n",pSendInfo);
  484. wsaoDecRef(pSendInfo);
  485. hr=DP_OK;
  486. } else {
  487. if (SOCKET_ERROR == err)
  488. {
  489. err = WSAGetLastError();
  490. if(err==WSA_IO_PENDING){
  491. hr=DPERR_PENDING;
  492. wsaoDecRef(pSendInfo);
  493. DPF(9,"ASYNC SEND Pending pSendInfo %x\n",pSendInfo);
  494. } else {
  495. if(err==WSAECONNRESET){
  496. hr=DPERR_CONNECTIONLOST;
  497. } else {
  498. hr=DPERR_GENERIC;
  499. }
  500. if(fAsync){
  501. // Got an error, need to dump 2 refs.
  502. pSendInfo->RefCount=1;
  503. pSendInfo->Status=hr;
  504. }
  505. wsaoDecRef(pSendInfo);
  506. // we got a socket from the bag. send failed,
  507. // so we're cruising it from the bag
  508. DPF(0,"send error - err = %d\n",err);
  509. DPF(4,"send failed - removing socket from bag");
  510. RemovePlayerFromSocketBag(pgd,pSendInfo->idTo);
  511. }
  512. }
  513. }
  514. } else {
  515. // Datagram Send
  516. DEBUGPRINTADDR(5,"unreliable send - sending to ",&pSendInfo->sockaddr);
  517. // send the message
  518. err = g_WSASendTo(pSendInfo->sSocket,
  519. (LPWSABUF)&pSendInfo->SendArray[pSendInfo->iFirstBuf],
  520. pSendInfo->cBuffers,
  521. &dwBytesSent,
  522. 0, /*flags*/
  523. (LPSOCKADDR)&pSendInfo->sockaddr,
  524. sizeof(SOCKADDR),
  525. (fAsync)?(&pSendInfo->wsao):NULL,
  526. (fAsync)?(SendComplete):NULL);
  527. if(!err){
  528. hr=DP_OK;
  529. wsaoDecRef(pSendInfo);
  530. } else {
  531. if (SOCKET_ERROR == err)
  532. {
  533. err = WSAGetLastError();
  534. if(err==WSA_IO_PENDING){
  535. hr=DPERR_PENDING;
  536. wsaoDecRef(pSendInfo);
  537. } else {
  538. hr=DPERR_GENERIC;
  539. if(fAsync){
  540. // some error, force completion.
  541. pSendInfo->RefCount=1;
  542. pSendInfo->Status=DPERR_GENERIC;
  543. }
  544. wsaoDecRef(pSendInfo);
  545. DPF(0,"send error - err = %d\n",err);
  546. }
  547. } else {
  548. DEBUG_BREAK();// SHOULD NEVER HAPPEN
  549. }
  550. }
  551. }
  552. return hr;
  553. #undef fAsync
  554. }
  555. // Alert thread provides a thread for send completions to run on.
  556. DWORD WINAPI SPSendThread(LPVOID lpv)
  557. {
  558. LPGLOBALDATA pgd=(LPGLOBALDATA) lpv;
  559. LPSENDINFO pSendInfo;
  560. DWORD rcWait=WAIT_IO_COMPLETION;
  561. BILINK *pBilink;
  562. BOOL bSent;
  563. pgd->BogusHandle=INVALID_HANDLE_VALUE; // workaround win95 wait for multiple bug.
  564. while(!pgd->bStopSendThread){
  565. rcWait=g_WSAWaitForMultipleEvents(1,&pgd->hSendWait,FALSE,INFINITE,TRUE);
  566. #ifdef DEBUG
  567. if(rcWait==WAIT_IO_COMPLETION){
  568. DPF(9,"ooooh, IO completion\n");
  569. }
  570. #endif
  571. do {
  572. bSent = FALSE;
  573. EnterCriticalSection(&pgd->csSendEx);
  574. pBilink=pgd->ReadyToSendQ.next;
  575. if(pBilink != &pgd->ReadyToSendQ){
  576. Delete(pBilink);
  577. LeaveCriticalSection(&pgd->csSendEx);
  578. pSendInfo=CONTAINING_RECORD(pBilink, SENDINFO, ReadyToSendQ);
  579. DoSend(pgd, pSendInfo);
  580. bSent=TRUE;
  581. } else {
  582. LeaveCriticalSection(&pgd->csSendEx);
  583. }
  584. } while (bSent);
  585. }
  586. pgd->bSendThreadRunning=FALSE;
  587. return FALSE;
  588. #undef hWait
  589. }
  590. void QueueForSend(LPGLOBALDATA pgd,LPSENDINFO pSendInfo)
  591. {
  592. EnterCriticalSection(&pgd->csSendEx);
  593. InsertBefore(&pSendInfo->ReadyToSendQ,&pgd->ReadyToSendQ);
  594. LeaveCriticalSection(&pgd->csSendEx);
  595. SetEvent(pgd->hSendWait);
  596. }
  597. // some common code for InternalReliableSendEx and UnreliableSendEx
  598. VOID CommonInitForSend(LPGLOBALDATA pgd,LPDPSP_SENDEXDATA psd,LPSENDINFO pSendInfo)
  599. {
  600. pSendInfo->dwMessageSize= psd->dwMessageSize;
  601. pSendInfo->dwUserContext= (DWORD_PTR)psd->lpDPContext;
  602. pSendInfo->RefCount = 2; // one for completion, 1 for this routine
  603. pSendInfo->pgd = pgd;
  604. pSendInfo->lpISP = psd->lpISP;
  605. pSendInfo->Status = DP_OK;
  606. pSendInfo->idTo = psd->idPlayerTo;
  607. pSendInfo->idFrom = psd->idPlayerFrom;
  608. pSendInfo->dwSendFlags = psd->dwFlags;
  609. if(psd->lpdwSPMsgID){
  610. *psd->lpdwSPMsgID=0;
  611. }
  612. EnterCriticalSection(&pgd->csSendEx);
  613. InsertBefore(&pSendInfo->PendingSendQ,&pgd->PendingSendQ);
  614. pgd->dwBytesPending += psd->dwMessageSize;
  615. pgd->dwMessagesPending += 1;
  616. LeaveCriticalSection(&pgd->csSendEx);
  617. }
  618. VOID UnpendSendInfo(LPGLOBALDATA pgd, LPSENDINFO pSendInfo)
  619. {
  620. EnterCriticalSection(&pgd->csSendEx);
  621. Delete(&pSendInfo->PendingSendQ);
  622. pgd->dwBytesPending -= pSendInfo->dwMessageSize;
  623. pgd->dwMessagesPending -= 1;
  624. LeaveCriticalSection(&pgd->csSendEx);
  625. }
  626. HRESULT InternalReliableSendEx(LPGLOBALDATA pgd, LPDPSP_SENDEXDATA psd, LPSENDINFO pSendInfo, SOCKADDR *lpSockAddr)
  627. {
  628. HRESULT hr;
  629. SOCKET sSocket = INVALID_SOCKET;
  630. BOOL fCreate=FALSE;
  631. // see if we have a connection already
  632. hr = GetSocketFromBag(pgd,&sSocket,psd->idPlayerTo,lpSockAddr);
  633. if(hr != DP_OK){
  634. hr=DPERR_GENERIC;
  635. return hr;
  636. }
  637. CommonInitForSend(pgd,psd,pSendInfo); // puts 2 refs on send.
  638. pSendInfo->dwFlags = SI_RELIABLE;
  639. pSendInfo->sSocket = sSocket;
  640. pSendInfo->iFirstBuf = 0;
  641. pSendInfo->cBuffers = psd->cBuffers+1;
  642. if(psd->dwFlags & DPSEND_ASYNC){
  643. QueueForSend(pgd,pSendInfo);
  644. hr=DPERR_PENDING;
  645. } else {
  646. hr=DoSend(pgd,pSendInfo);
  647. if(hr==DP_OK || hr==DPERR_PENDING){
  648. wsaoDecRef(pSendInfo);
  649. } else {
  650. // error,
  651. UnpendSendInfo(pgd, pSendInfo);
  652. }
  653. }
  654. return hr;
  655. }
  656. HRESULT UnreliableSendEx(LPDPSP_SENDEXDATA psd, LPSENDINFO pSendInfo)
  657. {
  658. SOCKADDR sockaddr;
  659. INT iAddrLen = sizeof(sockaddr);
  660. HRESULT hr=DP_OK;
  661. UINT err;
  662. DWORD dwSize = sizeof(SPPLAYERDATA);
  663. LPSPPLAYERDATA ppdTo;
  664. DWORD dwDataSize = sizeof(GLOBALDATA);
  665. LPGLOBALDATA pgd;
  666. BOOL bSendHeader;
  667. // get the global data
  668. hr =psd->lpISP->lpVtbl->GetSPData(psd->lpISP,(LPVOID *)&pgd,&dwDataSize,DPGET_LOCAL);
  669. if (FAILED(hr) || (dwDataSize != sizeof(GLOBALDATA) ))
  670. {
  671. DPF_ERR("couldn't get SP data from DirectPlay - failing");
  672. return E_FAIL;
  673. }
  674. if (pgd->iMaxUdpDg && (psd->dwMessageSize >= pgd->iMaxUdpDg))
  675. {
  676. return DPERR_SENDTOOBIG;
  677. }
  678. if (INVALID_SOCKET == pgd->sUnreliableSocket)
  679. {
  680. hr = CreateSocket(pgd,&(pgd->sUnreliableSocket),SOCK_DGRAM,0,INADDR_ANY,&err,FALSE);
  681. if (FAILED(hr))
  682. {
  683. DPF(0,"create unreliable send socket failed - err = %d\n",err);
  684. return hr;
  685. }
  686. }
  687. // get to address
  688. if (0 == psd->idPlayerTo)
  689. {
  690. sockaddr = pgd->saddrNS;
  691. }
  692. else
  693. {
  694. hr = psd->lpISP->lpVtbl->GetSPPlayerData(psd->lpISP,psd->idPlayerTo,&ppdTo,&dwSize,DPGET_REMOTE);
  695. if (FAILED(hr))
  696. {
  697. ASSERT(FALSE);
  698. return hr;
  699. }
  700. sockaddr = *(DGRAM_PSOCKADDR(ppdTo));
  701. }
  702. // put the token + size on front of the mesage
  703. SetMessageHeader((LPVOID)(pSendInfo->SendArray[0].buf),psd->dwMessageSize+sizeof(MESSAGEHEADER),TOKEN);
  704. bSendHeader=TRUE;
  705. if (psd->bSystemMessage)
  706. {
  707. SetReturnAddress(pSendInfo->SendArray[0].buf,SERVICE_SOCKET(pgd));
  708. } // reply
  709. else
  710. {
  711. // see if we can send this message w/ no header
  712. // if the message is smaller than a dword, or, if it's a valid sp header (fooling us
  713. // on the other end, don't send any header
  714. if ( !((psd->dwMessageSize >= sizeof(DWORD)) && !(VALID_SP_MESSAGE(pSendInfo->SendArray[0].buf))) )
  715. {
  716. bSendHeader=FALSE;
  717. }
  718. }
  719. CommonInitForSend(pgd,psd,pSendInfo);
  720. pSendInfo->dwFlags = SI_DATAGRAM;
  721. pSendInfo->sSocket = pgd->sUnreliableSocket;
  722. pSendInfo->sockaddr = sockaddr;
  723. if(bSendHeader){
  724. pSendInfo->iFirstBuf=0;
  725. pSendInfo->cBuffers =psd->cBuffers+1;
  726. } else {
  727. pSendInfo->iFirstBuf=1;
  728. pSendInfo->cBuffers=psd->cBuffers;
  729. }
  730. if(psd->dwFlags & DPSEND_ASYNC){
  731. QueueForSend(pgd,pSendInfo);
  732. hr=DPERR_PENDING;
  733. } else {
  734. hr=DoSend(pgd,pSendInfo);
  735. if(hr==DP_OK || hr==DPERR_PENDING){
  736. wsaoDecRef(pSendInfo);
  737. } else {
  738. UnpendSendInfo(pgd, pSendInfo);
  739. }
  740. }
  741. return hr;
  742. } // UnreliableSendEx
  743. #endif //SendEx