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