[WINE]
[reactos.git] / dll / win32 / msafd / misc / sndrcv.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Ancillary Function Driver DLL
4 * FILE: misc/sndrcv.c
5 * PURPOSE: Send/receive routines
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * Alex Ionescu (alex@relsoft.net)
8 * REVISIONS:
9 * CSH 01/09-2000 Created
10 * Alex 16/07/2004 - Complete Rewrite
11 */
12
13 #include <msafd.h>
14
15 #include <debug.h>
16
17 INT
18 WSPAPI
19 WSPAsyncSelect(IN SOCKET Handle,
20 IN HWND hWnd,
21 IN UINT wMsg,
22 IN LONG lEvent,
23 OUT LPINT lpErrno)
24 {
25 PSOCKET_INFORMATION Socket = NULL;
26 PASYNC_DATA AsyncData;
27 NTSTATUS Status;
28 ULONG BlockMode;
29
30 /* Get the Socket Structure associated to this Socket */
31 Socket = GetSocketStructure(Handle);
32 if (!Socket)
33 {
34 *lpErrno = WSAENOTSOCK;
35 return SOCKET_ERROR;
36 }
37
38 /* Allocate the Async Data Structure to pass on to the Thread later */
39 AsyncData = HeapAlloc(GetProcessHeap(), 0, sizeof(*AsyncData));
40 if (!AsyncData)
41 {
42 MsafdReturnWithErrno( STATUS_INSUFFICIENT_RESOURCES, lpErrno, 0, NULL );
43 return INVALID_SOCKET;
44 }
45
46 /* Change the Socket to Non Blocking */
47 BlockMode = 1;
48 SetSocketInformation(Socket, AFD_INFO_BLOCKING_MODE, &BlockMode, NULL);
49 Socket->SharedData.NonBlocking = TRUE;
50
51 /* Deactive WSPEventSelect */
52 if (Socket->SharedData.AsyncEvents)
53 {
54 if (WSPEventSelect(Handle, NULL, 0, lpErrno) == SOCKET_ERROR)
55 {
56 HeapFree(GetProcessHeap(), 0, AsyncData);
57 return SOCKET_ERROR;
58 }
59 }
60
61 /* Create the Asynch Thread if Needed */
62 SockCreateOrReferenceAsyncThread();
63
64 /* Open a Handle to AFD's Async Helper */
65 SockGetAsyncSelectHelperAfdHandle();
66
67 /* Store Socket Data */
68 Socket->SharedData.hWnd = hWnd;
69 Socket->SharedData.wMsg = wMsg;
70 Socket->SharedData.AsyncEvents = lEvent;
71 Socket->SharedData.AsyncDisabledEvents = 0;
72 Socket->SharedData.SequenceNumber++;
73
74 /* Return if there are no more Events */
75 if ((Socket->SharedData.AsyncEvents & (~Socket->SharedData.AsyncDisabledEvents)) == 0)
76 {
77 HeapFree(GetProcessHeap(), 0, AsyncData);
78 return 0;
79 }
80
81 /* Set up the Async Data */
82 AsyncData->ParentSocket = Socket;
83 AsyncData->SequenceNumber = Socket->SharedData.SequenceNumber;
84
85 /* Begin Async Select by using I/O Completion */
86 Status = NtSetIoCompletion(SockAsyncCompletionPort,
87 (PVOID)&SockProcessQueuedAsyncSelect,
88 AsyncData,
89 0,
90 0);
91
92 /* Return */
93 return ERROR_SUCCESS;
94 }
95
96
97 int
98 WSPAPI
99 WSPRecv(SOCKET Handle,
100 LPWSABUF lpBuffers,
101 DWORD dwBufferCount,
102 LPDWORD lpNumberOfBytesRead,
103 LPDWORD ReceiveFlags,
104 LPWSAOVERLAPPED lpOverlapped,
105 LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
106 LPWSATHREADID lpThreadId,
107 LPINT lpErrno)
108 {
109 PIO_STATUS_BLOCK IOSB;
110 IO_STATUS_BLOCK DummyIOSB;
111 AFD_RECV_INFO RecvInfo;
112 NTSTATUS Status;
113 PVOID APCContext;
114 PVOID APCFunction;
115 HANDLE Event = NULL;
116 HANDLE SockEvent;
117 PSOCKET_INFORMATION Socket;
118
119 AFD_DbgPrint(MID_TRACE,("Called (%x)\n", Handle));
120
121 /* Get the Socket Structure associate to this Socket*/
122 Socket = GetSocketStructure(Handle);
123 if (!Socket)
124 {
125 *lpErrno = WSAENOTSOCK;
126 return SOCKET_ERROR;
127 }
128
129 Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE,
130 NULL, 1, FALSE );
131
132 if( !NT_SUCCESS(Status) )
133 return -1;
134
135 /* Set up the Receive Structure */
136 RecvInfo.BufferArray = (PAFD_WSABUF)lpBuffers;
137 RecvInfo.BufferCount = dwBufferCount;
138 RecvInfo.TdiFlags = 0;
139 RecvInfo.AfdFlags = Socket->SharedData.NonBlocking ? AFD_IMMEDIATE : 0;
140
141 /* Set the TDI Flags */
142 if (*ReceiveFlags == 0)
143 {
144 RecvInfo.TdiFlags |= TDI_RECEIVE_NORMAL;
145 }
146 else
147 {
148 if (*ReceiveFlags & MSG_OOB)
149 {
150 RecvInfo.TdiFlags |= TDI_RECEIVE_EXPEDITED;
151 }
152
153 if (*ReceiveFlags & MSG_PEEK)
154 {
155 RecvInfo.TdiFlags |= TDI_RECEIVE_PEEK;
156 }
157
158 if (*ReceiveFlags & MSG_PARTIAL)
159 {
160 RecvInfo.TdiFlags |= TDI_RECEIVE_PARTIAL;
161 }
162 }
163
164 /* Verifiy if we should use APC */
165
166 if (lpOverlapped == NULL)
167 {
168 /* Not using Overlapped structure, so use normal blocking on event */
169 APCContext = NULL;
170 APCFunction = NULL;
171 Event = SockEvent;
172 IOSB = &DummyIOSB;
173 }
174 else
175 {
176 if (lpCompletionRoutine == NULL)
177 {
178 /* Using Overlapped Structure, but no Completition Routine, so no need for APC */
179 APCContext = lpOverlapped;
180 APCFunction = NULL;
181 Event = lpOverlapped->hEvent;
182 }
183 else
184 {
185 /* Using Overlapped Structure and a Completition Routine, so use an APC */
186 APCFunction = NULL; // should be a private io completition function inside us
187 APCContext = lpCompletionRoutine;
188 RecvInfo.AfdFlags |= AFD_SKIP_FIO;
189 }
190
191 IOSB = (PIO_STATUS_BLOCK)&lpOverlapped->Internal;
192 RecvInfo.AfdFlags |= AFD_OVERLAPPED;
193 }
194
195 IOSB->Status = STATUS_PENDING;
196
197 /* Send IOCTL */
198 Status = NtDeviceIoControlFile((HANDLE)Handle,
199 Event,
200 APCFunction,
201 APCContext,
202 IOSB,
203 IOCTL_AFD_RECV,
204 &RecvInfo,
205 sizeof(RecvInfo),
206 NULL,
207 0);
208
209 /* Wait for completition of not overlapped */
210 if (Status == STATUS_PENDING && lpOverlapped == NULL)
211 {
212 /* It's up to the protocol to time out recv. We must wait
213 * until the protocol decides it's had enough.
214 */
215 WaitForSingleObject(SockEvent, INFINITE);
216 Status = IOSB->Status;
217 }
218
219 NtClose( SockEvent );
220
221 AFD_DbgPrint(MID_TRACE,("Status %x Information %d\n", Status, IOSB->Information));
222
223 /* Return the Flags */
224 *ReceiveFlags = 0;
225
226 switch (Status)
227 {
228 case STATUS_RECEIVE_EXPEDITED:
229 *ReceiveFlags = MSG_OOB;
230 break;
231 case STATUS_RECEIVE_PARTIAL_EXPEDITED:
232 *ReceiveFlags = MSG_PARTIAL | MSG_OOB;
233 break;
234 case STATUS_RECEIVE_PARTIAL:
235 *ReceiveFlags = MSG_PARTIAL;
236 break;
237 }
238
239 /* Re-enable Async Event */
240 if (*ReceiveFlags & MSG_OOB)
241 {
242 SockReenableAsyncSelectEvent(Socket, FD_OOB);
243 }
244 else
245 {
246 SockReenableAsyncSelectEvent(Socket, FD_READ);
247 }
248
249 return MsafdReturnWithErrno ( Status, lpErrno, IOSB->Information, lpNumberOfBytesRead );
250 }
251
252 int
253 WSPAPI
254 WSPRecvFrom(SOCKET Handle,
255 LPWSABUF lpBuffers,
256 DWORD dwBufferCount,
257 LPDWORD lpNumberOfBytesRead,
258 LPDWORD ReceiveFlags,
259 struct sockaddr *SocketAddress,
260 int *SocketAddressLength,
261 LPWSAOVERLAPPED lpOverlapped,
262 LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
263 LPWSATHREADID lpThreadId,
264 LPINT lpErrno )
265 {
266 PIO_STATUS_BLOCK IOSB;
267 IO_STATUS_BLOCK DummyIOSB;
268 AFD_RECV_INFO_UDP RecvInfo;
269 NTSTATUS Status;
270 PVOID APCContext;
271 PVOID APCFunction;
272 HANDLE Event = NULL;
273 HANDLE SockEvent;
274 PSOCKET_INFORMATION Socket;
275
276 /* Get the Socket Structure associate to this Socket*/
277 Socket = GetSocketStructure(Handle);
278 if (!Socket)
279 {
280 *lpErrno = WSAENOTSOCK;
281 return SOCKET_ERROR;
282 }
283
284 Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE,
285 NULL, 1, FALSE );
286
287 if( !NT_SUCCESS(Status) )
288 return -1;
289
290 /* Set up the Receive Structure */
291 RecvInfo.BufferArray = (PAFD_WSABUF)lpBuffers;
292 RecvInfo.BufferCount = dwBufferCount;
293 RecvInfo.TdiFlags = 0;
294 RecvInfo.AfdFlags = Socket->SharedData.NonBlocking ? AFD_IMMEDIATE : 0;
295 RecvInfo.AddressLength = SocketAddressLength;
296 RecvInfo.Address = SocketAddress;
297
298 /* Set the TDI Flags */
299 if (*ReceiveFlags == 0)
300 {
301 RecvInfo.TdiFlags |= TDI_RECEIVE_NORMAL;
302 }
303 else
304 {
305 if (*ReceiveFlags & MSG_OOB)
306 {
307 RecvInfo.TdiFlags |= TDI_RECEIVE_EXPEDITED;
308 }
309
310 if (*ReceiveFlags & MSG_PEEK)
311 {
312 RecvInfo.TdiFlags |= TDI_RECEIVE_PEEK;
313 }
314
315 if (*ReceiveFlags & MSG_PARTIAL)
316 {
317 RecvInfo.TdiFlags |= TDI_RECEIVE_PARTIAL;
318 }
319 }
320
321 /* Verifiy if we should use APC */
322
323 if (lpOverlapped == NULL)
324 {
325 /* Not using Overlapped structure, so use normal blocking on event */
326 APCContext = NULL;
327 APCFunction = NULL;
328 Event = SockEvent;
329 IOSB = &DummyIOSB;
330 }
331 else
332 {
333 if (lpCompletionRoutine == NULL)
334 {
335 /* Using Overlapped Structure, but no Completition Routine, so no need for APC */
336 APCContext = lpOverlapped;
337 APCFunction = NULL;
338 Event = lpOverlapped->hEvent;
339 }
340 else
341 {
342 /* Using Overlapped Structure and a Completition Routine, so use an APC */
343 APCFunction = NULL; // should be a private io completition function inside us
344 APCContext = lpCompletionRoutine;
345 RecvInfo.AfdFlags |= AFD_SKIP_FIO;
346 }
347
348 IOSB = (PIO_STATUS_BLOCK)&lpOverlapped->Internal;
349 RecvInfo.AfdFlags |= AFD_OVERLAPPED;
350 }
351
352 IOSB->Status = STATUS_PENDING;
353
354 /* Send IOCTL */
355 Status = NtDeviceIoControlFile((HANDLE)Handle,
356 Event,
357 APCFunction,
358 APCContext,
359 IOSB,
360 IOCTL_AFD_RECV_DATAGRAM,
361 &RecvInfo,
362 sizeof(RecvInfo),
363 NULL,
364 0);
365
366 /* Wait for completition of not overlapped */
367 if (Status == STATUS_PENDING && lpOverlapped == NULL)
368 {
369 WaitForSingleObject(SockEvent, INFINITE); // BUGBUG, shouldn wait infintely for receive...
370 Status = IOSB->Status;
371 }
372
373 NtClose( SockEvent );
374
375 /* Return the Flags */
376 *ReceiveFlags = 0;
377
378 switch (Status)
379 {
380 case STATUS_RECEIVE_EXPEDITED: *ReceiveFlags = MSG_OOB;
381 break;
382 case STATUS_RECEIVE_PARTIAL_EXPEDITED:
383 *ReceiveFlags = MSG_PARTIAL | MSG_OOB;
384 break;
385 case STATUS_RECEIVE_PARTIAL:
386 *ReceiveFlags = MSG_PARTIAL;
387 break;
388 }
389
390 /* Re-enable Async Event */
391 SockReenableAsyncSelectEvent(Socket, FD_READ);
392
393 return MsafdReturnWithErrno ( Status, lpErrno, IOSB->Information, lpNumberOfBytesRead );
394 }
395
396
397 int
398 WSPAPI
399 WSPSend(SOCKET Handle,
400 LPWSABUF lpBuffers,
401 DWORD dwBufferCount,
402 LPDWORD lpNumberOfBytesSent,
403 DWORD iFlags,
404 LPWSAOVERLAPPED lpOverlapped,
405 LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
406 LPWSATHREADID lpThreadId,
407 LPINT lpErrno)
408 {
409 PIO_STATUS_BLOCK IOSB;
410 IO_STATUS_BLOCK DummyIOSB;
411 AFD_SEND_INFO SendInfo;
412 NTSTATUS Status;
413 PVOID APCContext;
414 PVOID APCFunction;
415 HANDLE Event = NULL;
416 HANDLE SockEvent;
417 PSOCKET_INFORMATION Socket;
418
419 /* Get the Socket Structure associate to this Socket*/
420 Socket = GetSocketStructure(Handle);
421 if (!Socket)
422 {
423 *lpErrno = WSAENOTSOCK;
424 return SOCKET_ERROR;
425 }
426
427 Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE,
428 NULL, 1, FALSE );
429
430 if( !NT_SUCCESS(Status) )
431 return -1;
432
433 AFD_DbgPrint(MID_TRACE,("Called\n"));
434
435 /* Set up the Send Structure */
436 SendInfo.BufferArray = (PAFD_WSABUF)lpBuffers;
437 SendInfo.BufferCount = dwBufferCount;
438 SendInfo.TdiFlags = 0;
439 SendInfo.AfdFlags = Socket->SharedData.NonBlocking ? AFD_IMMEDIATE : 0;
440
441 /* Set the TDI Flags */
442 if (iFlags)
443 {
444 if (iFlags & MSG_OOB)
445 {
446 SendInfo.TdiFlags |= TDI_SEND_EXPEDITED;
447 }
448 if (iFlags & MSG_PARTIAL)
449 {
450 SendInfo.TdiFlags |= TDI_SEND_PARTIAL;
451 }
452 }
453
454 /* Verifiy if we should use APC */
455 if (lpOverlapped == NULL)
456 {
457 /* Not using Overlapped structure, so use normal blocking on event */
458 APCContext = NULL;
459 APCFunction = NULL;
460 Event = SockEvent;
461 IOSB = &DummyIOSB;
462 }
463 else
464 {
465 if (lpCompletionRoutine == NULL)
466 {
467 /* Using Overlapped Structure, but no Completition Routine, so no need for APC */
468 APCContext = lpOverlapped;
469 APCFunction = NULL;
470 Event = lpOverlapped->hEvent;
471 }
472 else
473 {
474 /* Using Overlapped Structure and a Completition Routine, so use an APC */
475 APCFunction = NULL; // should be a private io completition function inside us
476 APCContext = lpCompletionRoutine;
477 SendInfo.AfdFlags |= AFD_SKIP_FIO;
478 }
479
480 IOSB = (PIO_STATUS_BLOCK)&lpOverlapped->Internal;
481 SendInfo.AfdFlags |= AFD_OVERLAPPED;
482 }
483
484 IOSB->Status = STATUS_PENDING;
485
486 /* Send IOCTL */
487 Status = NtDeviceIoControlFile((HANDLE)Handle,
488 Event,
489 APCFunction,
490 APCContext,
491 IOSB,
492 IOCTL_AFD_SEND,
493 &SendInfo,
494 sizeof(SendInfo),
495 NULL,
496 0);
497
498 /* Wait for completition of not overlapped */
499 if (Status == STATUS_PENDING && lpOverlapped == NULL)
500 {
501 WaitForSingleObject(SockEvent, INFINITE); // BUGBUG, shouldn wait infintely for send...
502 Status = IOSB->Status;
503 }
504
505 NtClose( SockEvent );
506
507 if (Status == STATUS_PENDING)
508 {
509 AFD_DbgPrint(MID_TRACE,("Leaving (Pending)\n"));
510 return MsafdReturnWithErrno(Status, lpErrno, IOSB->Information, lpNumberOfBytesSent);
511 }
512
513 /* Re-enable Async Event */
514 SockReenableAsyncSelectEvent(Socket, FD_WRITE);
515
516 AFD_DbgPrint(MID_TRACE,("Leaving (Success, %d)\n", IOSB->Information));
517
518 return MsafdReturnWithErrno( Status, lpErrno, IOSB->Information, lpNumberOfBytesSent );
519 }
520
521 int
522 WSPAPI
523 WSPSendTo(SOCKET Handle,
524 LPWSABUF lpBuffers,
525 DWORD dwBufferCount,
526 LPDWORD lpNumberOfBytesSent,
527 DWORD iFlags,
528 const struct sockaddr *SocketAddress,
529 int SocketAddressLength,
530 LPWSAOVERLAPPED lpOverlapped,
531 LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
532 LPWSATHREADID lpThreadId,
533 LPINT lpErrno)
534 {
535 PIO_STATUS_BLOCK IOSB;
536 IO_STATUS_BLOCK DummyIOSB;
537 AFD_SEND_INFO_UDP SendInfo;
538 NTSTATUS Status;
539 PVOID APCContext;
540 PVOID APCFunction;
541 HANDLE Event = NULL;
542 PTRANSPORT_ADDRESS RemoteAddress;
543 PSOCKADDR BindAddress = NULL;
544 INT BindAddressLength;
545 HANDLE SockEvent;
546 PSOCKET_INFORMATION Socket;
547
548 /* Get the Socket Structure associate to this Socket */
549 Socket = GetSocketStructure(Handle);
550 if (!Socket)
551 {
552 *lpErrno = WSAENOTSOCK;
553 return SOCKET_ERROR;
554 }
555
556 /* Bind us First */
557 if (Socket->SharedData.State == SocketOpen)
558 {
559 /* Get the Wildcard Address */
560 BindAddressLength = Socket->HelperData->MaxWSAddressLength;
561 BindAddress = HeapAlloc(GlobalHeap, 0, BindAddressLength);
562 if (!BindAddress)
563 {
564 MsafdReturnWithErrno(STATUS_INSUFFICIENT_RESOURCES, lpErrno, 0, NULL);
565 return INVALID_SOCKET;
566 }
567
568 Socket->HelperData->WSHGetWildcardSockaddr(Socket->HelperContext,
569 BindAddress,
570 &BindAddressLength);
571 /* Bind it */
572 if (WSPBind(Handle, BindAddress, BindAddressLength, lpErrno) == SOCKET_ERROR)
573 return SOCKET_ERROR;
574 }
575
576 RemoteAddress = HeapAlloc(GlobalHeap, 0, 0x6 + SocketAddressLength);
577 if (!RemoteAddress)
578 {
579 if (BindAddress != NULL)
580 {
581 HeapFree(GlobalHeap, 0, BindAddress);
582 }
583 return MsafdReturnWithErrno(STATUS_INSUFFICIENT_RESOURCES, lpErrno, 0, NULL);
584 }
585
586 Status = NtCreateEvent(&SockEvent,
587 GENERIC_READ | GENERIC_WRITE,
588 NULL, 1, FALSE);
589
590 if (!NT_SUCCESS(Status))
591 {
592 HeapFree(GlobalHeap, 0, RemoteAddress);
593 if (BindAddress != NULL)
594 {
595 HeapFree(GlobalHeap, 0, BindAddress);
596 }
597 return SOCKET_ERROR;
598 }
599
600 /* Set up Address in TDI Format */
601 RemoteAddress->TAAddressCount = 1;
602 RemoteAddress->Address[0].AddressLength = SocketAddressLength - sizeof(SocketAddress->sa_family);
603 RtlCopyMemory(&RemoteAddress->Address[0].AddressType, SocketAddress, SocketAddressLength);
604
605 /* Set up Structure */
606 SendInfo.BufferArray = (PAFD_WSABUF)lpBuffers;
607 SendInfo.AfdFlags = Socket->SharedData.NonBlocking ? AFD_IMMEDIATE : 0;
608 SendInfo.BufferCount = dwBufferCount;
609 SendInfo.TdiConnection.RemoteAddress = RemoteAddress;
610 SendInfo.TdiConnection.RemoteAddressLength = Socket->HelperData->MaxTDIAddressLength;
611
612 /* Verifiy if we should use APC */
613 if (lpOverlapped == NULL)
614 {
615 /* Not using Overlapped structure, so use normal blocking on event */
616 APCContext = NULL;
617 APCFunction = NULL;
618 Event = SockEvent;
619 IOSB = &DummyIOSB;
620 }
621 else
622 {
623 if (lpCompletionRoutine == NULL)
624 {
625 /* Using Overlapped Structure, but no Completition Routine, so no need for APC */
626 APCContext = lpOverlapped;
627 APCFunction = NULL;
628 Event = lpOverlapped->hEvent;
629 }
630 else
631 {
632 /* Using Overlapped Structure and a Completition Routine, so use an APC */
633 /* Should be a private io completition function inside us */
634 APCFunction = NULL;
635 APCContext = lpCompletionRoutine;
636 SendInfo.AfdFlags |= AFD_SKIP_FIO;
637 }
638
639 IOSB = (PIO_STATUS_BLOCK)&lpOverlapped->Internal;
640 SendInfo.AfdFlags |= AFD_OVERLAPPED;
641 }
642
643 /* Send IOCTL */
644 Status = NtDeviceIoControlFile((HANDLE)Handle,
645 Event,
646 APCFunction,
647 APCContext,
648 IOSB,
649 IOCTL_AFD_SEND_DATAGRAM,
650 &SendInfo,
651 sizeof(SendInfo),
652 NULL,
653 0);
654
655 /* Wait for completition of not overlapped */
656 if (Status == STATUS_PENDING && lpOverlapped == NULL)
657 {
658 /* BUGBUG, shouldn't wait infintely for send... */
659 WaitForSingleObject(SockEvent, INFINITE);
660 Status = IOSB->Status;
661 }
662
663 NtClose(SockEvent);
664 HeapFree(GlobalHeap, 0, RemoteAddress);
665 if (BindAddress != NULL)
666 {
667 HeapFree(GlobalHeap, 0, BindAddress);
668 }
669
670 if (Status != STATUS_PENDING)
671 SockReenableAsyncSelectEvent(Socket, FD_WRITE);
672
673 return MsafdReturnWithErrno(Status, lpErrno, IOSB->Information, lpNumberOfBytesSent);
674 }
675
676 INT
677 WSPAPI
678 WSPRecvDisconnect(IN SOCKET s,
679 OUT LPWSABUF lpInboundDisconnectData,
680 OUT LPINT lpErrno)
681 {
682 UNIMPLEMENTED
683 return 0;
684 }
685
686
687
688 INT
689 WSPAPI
690 WSPSendDisconnect(IN SOCKET s,
691 IN LPWSABUF lpOutboundDisconnectData,
692 OUT LPINT lpErrno)
693 {
694 UNIMPLEMENTED
695 return 0;
696 }
697
698 /* EOF */