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.

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