2 * WSOCK32 specific functions
4 * Copyright (C) 1993,1994,1996,1997 John Brezak, Erik Bos, Alex Korobka.
5 * Copyright (C) 2003 Juan Lang.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include "wine/debug.h"
29 #include "wscontrol.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(winsock
);
34 /* internal remapper function for the IP_ constants */
35 static INT
_remap_optname(INT level
, INT optname
)
37 TRACE("level=%d, optname=%d\n", level
, optname
);
38 if (level
== IPPROTO_IP
) {
39 switch (optname
) { /***** from value *****/
40 case 2: return 9; /* IP_MULTICAST_IF */
41 case 3: return 10; /* IP_MULTICAST_TTL */
42 case 4: return 11; /* IP_MULTICAST_LOOP */
43 case 5: return 12; /* IP_ADD_MEMBERSHIP */
44 case 6: return 13; /* IP_DROP_MEMBERSHIP */
45 case 7: return 4; /* IP_TTL */
46 case 8: return 3; /* IP_TOS */
47 case 9: return 14; /* IP_DONTFRAGMENT */
48 default: FIXME("Unknown optname %d, can't remap!\n", optname
); return optname
;
51 /* don't need to do anything */
56 /***********************************************************************
57 * setsockopt (WSOCK32.21)
59 * We have these forwarders because, for reasons unknown to us mere mortals,
60 * the values of the IP_ constants changed between winsock.h and winsock2.h.
61 * So, we need to remap them here.
63 INT WINAPI
WS1_setsockopt(SOCKET s
, INT level
, INT optname
, char *optval
, INT optlen
)
65 return setsockopt(s
, level
, _remap_optname(level
, optname
), optval
, optlen
);
68 /***********************************************************************
69 * getsockopt (WSOCK32.7)
71 INT WINAPI
WS1_getsockopt(SOCKET s
, INT level
, INT optname
, char *optval
, INT
*optlen
)
73 return getsockopt(s
, level
, _remap_optname(level
, optname
), optval
, optlen
);
76 /***********************************************************************
77 * WsControl (WSOCK32.1001)
79 * WsControl seems to be an undocumented Win95 function. A lot of
80 * discussion about WsControl can be found on the net, e.g.
81 * Subject: Re: WSOCK32.DLL WsControl Exported Function
82 * From: "Peter Rindfuss" <rindfuss-s@medea.wz-berlin.de>
85 * The WSCNTL_TCPIP_QUERY_INFO option is partially implemented based
86 * on observing the behaviour of WsControl with an app in
87 * Windows 98. It is not fully implemented, and there could
88 * be (are?) errors due to incorrect assumptions made.
91 * WsControl returns WSCTL_SUCCESS on success.
92 * ERROR_LOCK_VIOLATION is returned if the output buffer length
93 * (*pcbResponseInfoLen) is too small. This is an unusual error code, but
94 * it matches Win98's behavior. Other errors come from winerror.h, not from
95 * winsock.h. Again, this is to match Win98 behavior.
99 DWORD WINAPI
WsControl(DWORD protocol
,
102 LPDWORD pcbRequestInfoLen
,
103 LPVOID pResponseInfo
,
104 LPDWORD pcbResponseInfoLen
)
107 /* Get the command structure into a pointer we can use,
109 TDIObjectID
*pcommand
= pRequestInfo
;
111 /* validate input parameters. Error codes are from winerror.h, not from
112 * winsock.h. pcbResponseInfoLen is apparently allowed to be NULL for some
113 * commands, since winipcfg.exe fails if we ensure it's non-NULL in every
116 if (protocol
!= IPPROTO_TCP
) return ERROR_INVALID_PARAMETER
;
117 if (!pcommand
) return ERROR_INVALID_PARAMETER
;
118 if (!pcbRequestInfoLen
) return ERROR_INVALID_ACCESS
;
119 if (*pcbRequestInfoLen
< sizeof(TDIObjectID
)) return ERROR_INVALID_ACCESS
;
120 if (!pResponseInfo
) return ERROR_INVALID_PARAMETER
;
121 if (pcommand
->toi_type
!= INFO_TYPE_PROVIDER
) return ERROR_INVALID_PARAMETER
;
123 TRACE (" WsControl TOI_ID=>0x%lx<, {TEI_ENTITY=0x%lx, TEI_INSTANCE=0x%lx}, TOI_CLASS=0x%lx, TOI_TYPE=0x%lx\n",
124 pcommand
->toi_id
, pcommand
->toi_entity
.tei_entity
,
125 pcommand
->toi_entity
.tei_instance
,
126 pcommand
->toi_class
, pcommand
->toi_type
);
130 case WSCNTL_TCPIP_QUERY_INFO
:
132 if (pcommand
->toi_class
!= INFO_CLASS_GENERIC
&&
133 pcommand
->toi_class
!= INFO_CLASS_PROTOCOL
)
135 ERR("Unexpected class %ld for WSCNTL_TCPIP_QUERY_INFO\n",
136 pcommand
->toi_class
);
137 return ERROR_BAD_ENVIRONMENT
;
140 switch (pcommand
->toi_id
)
142 /* ENTITY_LIST_ID gets the list of "entity IDs", where an entity
143 may represent an interface, or a datagram service, or address
144 translation, or other fun things. Typically an entity ID represents
145 a class of service, which is further queried for what type it is.
146 Different types will then have more specific queries defined.
150 TDIEntityID
*baseptr
= pResponseInfo
;
151 DWORD numInt
, i
, ifTable
, spaceNeeded
;
154 if (!pcbResponseInfoLen
)
155 return ERROR_BAD_ENVIRONMENT
;
156 if (pcommand
->toi_class
!= INFO_CLASS_GENERIC
)
158 FIXME ("Unexpected Option for ENTITY_LIST_ID request -> toi_class=0x%lx\n",
159 pcommand
->toi_class
);
160 return (ERROR_BAD_ENVIRONMENT
);
163 GetNumberOfInterfaces(&numInt
);
164 spaceNeeded
= sizeof(TDIEntityID
) * (numInt
* 2 + 3);
166 if (*pcbResponseInfoLen
< spaceNeeded
)
167 return (ERROR_LOCK_VIOLATION
);
170 GetIfTable(NULL
, &ifTable
, FALSE
);
171 table
= HeapAlloc( GetProcessHeap(), 0, ifTable
);
173 return ERROR_NOT_ENOUGH_MEMORY
;
174 GetIfTable(table
, &ifTable
, FALSE
);
176 spaceNeeded
= sizeof(TDIEntityID
) * (table
->dwNumEntries
+ 4);
177 if (*pcbResponseInfoLen
< spaceNeeded
)
179 HeapFree( GetProcessHeap(), 0, table
);
180 return ERROR_LOCK_VIOLATION
;
183 memset(baseptr
, 0, spaceNeeded
);
185 for (i
= 0; i
< table
->dwNumEntries
; i
++)
187 /* Return IF_GENERIC and CL_NL_ENTITY on every interface, and
188 * AT_ENTITY, CL_TL_ENTITY, and CO_TL_ENTITY on the first
189 * interface. MS returns them only on the loopback interface,
190 * but it doesn't seem to matter.
194 baseptr
->tei_entity
= CO_TL_ENTITY
;
195 baseptr
->tei_instance
= table
->table
[i
].dwIndex
;
197 baseptr
->tei_entity
= CL_TL_ENTITY
;
198 baseptr
->tei_instance
= table
->table
[i
].dwIndex
;
200 baseptr
->tei_entity
= AT_ENTITY
;
201 baseptr
->tei_instance
= table
->table
[i
].dwIndex
;
204 baseptr
->tei_entity
= CL_NL_ENTITY
;
205 baseptr
->tei_instance
= table
->table
[i
].dwIndex
;
207 baseptr
->tei_entity
= IF_GENERIC
;
208 baseptr
->tei_instance
= table
->table
[i
].dwIndex
;
212 *pcbResponseInfoLen
= spaceNeeded
;
213 HeapFree( GetProcessHeap(), 0, table
);
217 /* Returns MIB-II statistics for an interface */
219 switch (pcommand
->toi_entity
.tei_entity
)
222 if (pcommand
->toi_class
== INFO_CLASS_GENERIC
)
224 if (!pcbResponseInfoLen
)
225 return ERROR_BAD_ENVIRONMENT
;
226 *((ULONG
*)pResponseInfo
) = IF_MIB
;
227 *pcbResponseInfoLen
= sizeof(ULONG
);
229 else if (pcommand
->toi_class
== INFO_CLASS_PROTOCOL
)
232 DWORD index
= pcommand
->toi_entity
.tei_instance
, ret
;
233 DWORD size
= sizeof(row
) - sizeof(row
.wszName
) -
236 if (!pcbResponseInfoLen
)
237 return ERROR_BAD_ENVIRONMENT
;
238 if (*pcbResponseInfoLen
< size
)
239 return (ERROR_LOCK_VIOLATION
);
241 ret
= GetIfEntry(&row
);
244 /* FIXME: Win98's arp.exe insists on querying index 1 for
245 * its MIB-II stats, regardless of the tei_instances
246 * returned in the ENTITY_LIST query above. If the query
247 * fails, arp.exe fails. So, I do this hack return value
248 * if index is 1 and the query failed just to get arp.exe
253 ERR ("Error retrieving data for interface index %lu\n",
257 size
= sizeof(row
) - sizeof(row
.wszName
) -
258 sizeof(row
.bDescr
) + row
.dwDescrLen
;
259 if (*pcbResponseInfoLen
< size
)
260 return (ERROR_LOCK_VIOLATION
);
261 memcpy(pResponseInfo
, &row
.dwIndex
, size
);
262 *pcbResponseInfoLen
= size
;
266 /* Returns address-translation related data. In our case, this is
270 if (pcommand
->toi_class
== INFO_CLASS_GENERIC
)
272 if (!pcbResponseInfoLen
)
273 return ERROR_BAD_ENVIRONMENT
;
274 *((ULONG
*)pResponseInfo
) = AT_ARP
;
275 *pcbResponseInfoLen
= sizeof(ULONG
);
277 else if (pcommand
->toi_class
== INFO_CLASS_PROTOCOL
)
279 PMIB_IPNETTABLE table
;
281 PULONG output
= pResponseInfo
;
283 if (!pcbResponseInfoLen
)
284 return ERROR_BAD_ENVIRONMENT
;
285 if (*pcbResponseInfoLen
< sizeof(ULONG
) * 2)
286 return (ERROR_LOCK_VIOLATION
);
287 GetIpNetTable(NULL
, &size
, FALSE
);
288 table
= HeapAlloc( GetProcessHeap(), 0, size
);
290 return ERROR_NOT_ENOUGH_MEMORY
;
291 GetIpNetTable(table
, &size
, FALSE
);
292 /* FIXME: I don't understand the meaning of the ARP output
293 * very well, but it seems to indicate how many ARP entries
294 * exist. I don't know whether this should reflect the
295 * number per interface, as I'm only testing with a single
296 * interface. So, I lie and say all ARP entries exist on
297 * a single interface--the first one that appears in the
300 *(output
++) = table
->dwNumEntries
;
301 *output
= table
->table
[0].dwIndex
;
302 HeapFree( GetProcessHeap(), 0, table
);
303 *pcbResponseInfoLen
= sizeof(ULONG
) * 2;
307 /* Returns connectionless network layer statistics--in our case,
311 if (pcommand
->toi_class
== INFO_CLASS_GENERIC
)
313 if (!pcbResponseInfoLen
)
314 return ERROR_BAD_ENVIRONMENT
;
315 *((ULONG
*)pResponseInfo
) = CL_NL_IP
;
316 *pcbResponseInfoLen
= sizeof(ULONG
);
318 else if (pcommand
->toi_class
== INFO_CLASS_PROTOCOL
)
320 if (!pcbResponseInfoLen
)
321 return ERROR_BAD_ENVIRONMENT
;
322 if (*pcbResponseInfoLen
< sizeof(MIB_IPSTATS
))
323 return ERROR_LOCK_VIOLATION
;
324 GetIpStatistics(pResponseInfo
);
326 *pcbResponseInfoLen
= sizeof(MIB_IPSTATS
);
330 /* Returns connectionless transport layer statistics--in our case,
334 if (pcommand
->toi_class
== INFO_CLASS_GENERIC
)
336 if (!pcbResponseInfoLen
)
337 return ERROR_BAD_ENVIRONMENT
;
338 *((ULONG
*)pResponseInfo
) = CL_TL_UDP
;
339 *pcbResponseInfoLen
= sizeof(ULONG
);
341 else if (pcommand
->toi_class
== INFO_CLASS_PROTOCOL
)
343 if (!pcbResponseInfoLen
)
344 return ERROR_BAD_ENVIRONMENT
;
345 if (*pcbResponseInfoLen
< sizeof(MIB_UDPSTATS
))
346 return ERROR_LOCK_VIOLATION
;
347 GetUdpStatistics(pResponseInfo
);
348 *pcbResponseInfoLen
= sizeof(MIB_UDPSTATS
);
352 /* Returns connection-oriented transport layer statistics--in our
356 if (pcommand
->toi_class
== INFO_CLASS_GENERIC
)
358 if (!pcbResponseInfoLen
)
359 return ERROR_BAD_ENVIRONMENT
;
360 *((ULONG
*)pResponseInfo
) = CO_TL_TCP
;
361 *pcbResponseInfoLen
= sizeof(ULONG
);
363 else if (pcommand
->toi_class
== INFO_CLASS_PROTOCOL
)
365 if (!pcbResponseInfoLen
)
366 return ERROR_BAD_ENVIRONMENT
;
367 if (*pcbResponseInfoLen
< sizeof(MIB_TCPSTATS
))
368 return ERROR_LOCK_VIOLATION
;
369 GetTcpStatistics(pResponseInfo
);
370 *pcbResponseInfoLen
= sizeof(MIB_TCPSTATS
);
375 ERR("Unknown entity %ld for ENTITY_TYPE_ID query\n",
376 pcommand
->toi_entity
.tei_entity
);
380 /* This call returns the IP address, subnet mask, and broadcast
381 * address for an interface. If there are multiple IP addresses for
382 * the interface with the given index, returns the "first" one.
384 case IP_MIB_ADDRTABLE_ENTRY_ID
:
386 DWORD index
= pcommand
->toi_entity
.tei_instance
;
387 PMIB_IPADDRROW baseIPInfo
= pResponseInfo
;
388 PMIB_IPADDRTABLE table
;
391 if (!pcbResponseInfoLen
)
392 return ERROR_BAD_ENVIRONMENT
;
393 if (*pcbResponseInfoLen
< sizeof(MIB_IPADDRROW
))
394 return (ERROR_LOCK_VIOLATION
);
396 /* get entire table, because there isn't an exported function that
397 gets just one entry. */
399 GetIpAddrTable(NULL
, &tableSize
, FALSE
);
400 table
= HeapAlloc( GetProcessHeap(), 0, tableSize
);
402 return ERROR_NOT_ENOUGH_MEMORY
;
403 GetIpAddrTable(table
, &tableSize
, FALSE
);
404 for (i
= 0; i
< table
->dwNumEntries
; i
++)
406 if (table
->table
[i
].dwIndex
== index
)
408 TRACE("Found IP info for tei_instance 0x%lx:\n", index
);
409 TRACE("IP 0x%08lx, mask 0x%08lx\n", table
->table
[i
].dwAddr
,
410 table
->table
[i
].dwMask
);
411 *baseIPInfo
= table
->table
[i
];
415 HeapFree( GetProcessHeap(), 0, table
);
417 *pcbResponseInfoLen
= sizeof(MIB_IPADDRROW
);
421 case IP_MIB_TABLE_ENTRY_ID
:
423 switch (pcommand
->toi_entity
.tei_entity
)
425 /* This call returns the routing table.
426 * No official documentation found, even the name of the command is unknown.
428 * http://www.cyberport.com/~tangent/programming/winsock/articles/wscontrol.html
429 * and testings done with winipcfg.exe, route.exe and ipconfig.exe.
430 * pcommand->toi_entity.tei_instance seems to be the interface number
431 * but route.exe outputs only the information for the last interface
432 * if only the routes for the pcommand->toi_entity.tei_instance
433 * interface are returned. */
436 DWORD routeTableSize
, numRoutes
, ndx
, ret
;
437 PMIB_IPFORWARDTABLE table
;
438 IPRouteEntry
*winRouteTable
= pResponseInfo
;
440 if (!pcbResponseInfoLen
)
441 return ERROR_BAD_ENVIRONMENT
;
442 ret
= GetIpForwardTable(NULL
, &routeTableSize
, FALSE
);
443 if (ret
!= ERROR_INSUFFICIENT_BUFFER
)
445 numRoutes
= (routeTableSize
- sizeof(MIB_IPFORWARDTABLE
))
446 / sizeof(MIB_IPFORWARDROW
) + 1;
447 if (*pcbResponseInfoLen
< sizeof(IPRouteEntry
) * numRoutes
)
448 return (ERROR_LOCK_VIOLATION
);
449 table
= HeapAlloc( GetProcessHeap(), 0, routeTableSize
);
451 return ERROR_NOT_ENOUGH_MEMORY
;
452 ret
= GetIpForwardTable(table
, &routeTableSize
, FALSE
);
453 if (ret
!= NO_ERROR
) {
454 HeapFree( GetProcessHeap(), 0, table
);
458 memset(pResponseInfo
, 0, sizeof(IPRouteEntry
) * numRoutes
);
459 for (ndx
= 0; ndx
< table
->dwNumEntries
; ndx
++)
461 winRouteTable
->ire_addr
= table
->table
[ndx
].dwForwardDest
;
462 winRouteTable
->ire_index
=
463 table
->table
[ndx
].dwForwardIfIndex
;
464 winRouteTable
->ire_metric
=
465 table
->table
[ndx
].dwForwardMetric1
;
466 /* winRouteTable->ire_option4 =
467 winRouteTable->ire_option5 =
468 winRouteTable->ire_option6 = */
469 winRouteTable
->ire_gw
= table
->table
[ndx
].dwForwardNextHop
;
470 /* winRouteTable->ire_option8 =
471 winRouteTable->ire_option9 =
472 winRouteTable->ire_option10 = */
473 winRouteTable
->ire_mask
= table
->table
[ndx
].dwForwardMask
;
474 /* winRouteTable->ire_option12 = */
478 /* calculate the length of the data in the output buffer */
479 *pcbResponseInfoLen
= sizeof(IPRouteEntry
) *
482 HeapFree( GetProcessHeap(), 0, table
);
488 DWORD arpTableSize
, numEntries
, ret
;
489 PMIB_IPNETTABLE table
;
491 if (!pcbResponseInfoLen
)
492 return ERROR_BAD_ENVIRONMENT
;
493 ret
= GetIpNetTable(NULL
, &arpTableSize
, FALSE
);
494 if (ret
!= ERROR_INSUFFICIENT_BUFFER
)
496 numEntries
= (arpTableSize
- sizeof(MIB_IPNETTABLE
))
497 / sizeof(MIB_IPNETROW
) + 1;
498 if (*pcbResponseInfoLen
< sizeof(MIB_IPNETROW
) * numEntries
)
499 return (ERROR_LOCK_VIOLATION
);
500 table
= HeapAlloc( GetProcessHeap(), 0, arpTableSize
);
502 return ERROR_NOT_ENOUGH_MEMORY
;
503 ret
= GetIpNetTable(table
, &arpTableSize
, FALSE
);
504 if (ret
!= NO_ERROR
) {
505 HeapFree( GetProcessHeap(), 0, table
);
508 if (*pcbResponseInfoLen
< sizeof(MIB_IPNETROW
) *
511 HeapFree( GetProcessHeap(), 0, table
);
512 return ERROR_LOCK_VIOLATION
;
514 memcpy(pResponseInfo
, table
->table
, sizeof(MIB_IPNETROW
) *
515 table
->dwNumEntries
);
517 /* calculate the length of the data in the output buffer */
518 *pcbResponseInfoLen
= sizeof(MIB_IPNETROW
) *
521 HeapFree( GetProcessHeap(), 0, table
);
527 DWORD tcpTableSize
, numEntries
, ret
;
531 if (!pcbResponseInfoLen
)
532 return ERROR_BAD_ENVIRONMENT
;
533 ret
= GetTcpTable(NULL
, &tcpTableSize
, FALSE
);
534 if (ret
!= ERROR_INSUFFICIENT_BUFFER
)
536 numEntries
= (tcpTableSize
- sizeof(MIB_TCPTABLE
))
537 / sizeof(MIB_TCPROW
) + 1;
538 if (*pcbResponseInfoLen
< sizeof(MIB_TCPROW
) * numEntries
)
539 return (ERROR_LOCK_VIOLATION
);
540 table
= HeapAlloc( GetProcessHeap(), 0, tcpTableSize
);
542 return ERROR_NOT_ENOUGH_MEMORY
;
543 ret
= GetTcpTable(table
, &tcpTableSize
, FALSE
);
544 if (ret
!= NO_ERROR
) {
545 HeapFree( GetProcessHeap(), 0, table
);
548 if (*pcbResponseInfoLen
< sizeof(MIB_TCPROW
) *
551 HeapFree( GetProcessHeap(), 0, table
);
552 return ERROR_LOCK_VIOLATION
;
554 for (i
= 0; i
< table
->dwNumEntries
; i
++)
558 sPort
= ntohs((USHORT
)table
->table
[i
].dwLocalPort
);
559 table
->table
[i
].dwLocalPort
= (DWORD
)sPort
;
560 sPort
= ntohs((USHORT
)table
->table
[i
].dwRemotePort
);
561 table
->table
[i
].dwRemotePort
= (DWORD
)sPort
;
563 memcpy(pResponseInfo
, table
->table
, sizeof(MIB_TCPROW
) *
564 table
->dwNumEntries
);
566 /* calculate the length of the data in the output buffer */
567 *pcbResponseInfoLen
= sizeof(MIB_TCPROW
) *
570 HeapFree( GetProcessHeap(), 0, table
);
576 FIXME ("Command ID Not Supported -> toi_id=0x%lx, toi_entity={tei_entity=0x%lx, tei_instance=0x%lx}, toi_class=0x%lx\n",
577 pcommand
->toi_id
, pcommand
->toi_entity
.tei_entity
,
578 pcommand
->toi_entity
.tei_instance
, pcommand
->toi_class
);
580 return (ERROR_BAD_ENVIRONMENT
);
589 FIXME ("Command ID Not Supported -> toi_id=0x%lx, toi_entity={tei_entity=0x%lx, tei_instance=0x%lx}, toi_class=0x%lx\n",
590 pcommand
->toi_id
, pcommand
->toi_entity
.tei_entity
,
591 pcommand
->toi_entity
.tei_instance
, pcommand
->toi_class
);
593 return (ERROR_BAD_ENVIRONMENT
);
600 case WSCNTL_TCPIP_ICMP_ECHO
:
602 unsigned int addr
= *(unsigned int*)pRequestInfo
;
604 int timeout
= *(unsigned int*)(inbuf
+4);
605 short x1
= *(unsigned short*)(inbuf
+8);
606 short sendbufsize
= *(unsigned short*)(inbuf
+10);
607 char x2
= *(unsigned char*)(inbuf
+12);
608 char ttl
= *(unsigned char*)(inbuf
+13);
609 char service
= *(unsigned char*)(inbuf
+14);
610 char type
= *(unsigned char*)(inbuf
+15); /* 0x2: don't fragment*/
613 FIXME("(ICMP_ECHO) to 0x%08x stub\n", addr
);
618 FIXME("Protocol Not Supported -> protocol=0x%lx, action=0x%lx, Request=%p, RequestLen=%p, Response=%p, ResponseLen=%p\n",
619 protocol
, action
, pRequestInfo
, pcbRequestInfoLen
, pResponseInfo
, pcbResponseInfoLen
);
621 return (WSAEOPNOTSUPP
);
625 return (WSCTL_SUCCESS
);
630 /***********************************************************************
631 * WSARecvEx (WSOCK32.1107)
633 * WSARecvEx is a Microsoft specific extension to winsock that is identical to recv
634 * except that has an in/out argument call flags that has the value MSG_PARTIAL ored
635 * into the flags parameter when a partial packet is read. This only applies to
636 * sockets using the datagram protocol. This method does not seem to be implemented
637 * correctly by microsoft as the winsock implementation does not set the MSG_PARTIAL
638 * flag when a fragmented packet arrives.
640 INT WINAPI
WSARecvEx(SOCKET s
, char *buf
, INT len
, INT
*flags
)
642 FIXME("(WSARecvEx) partial packet return value not set\n");
643 return recv(s
, buf
, len
, *flags
);
647 /***********************************************************************
648 * s_perror (WSOCK32.1108)
650 void WINAPI
s_perror(LPCSTR message
)
652 FIXME("(%s): stub\n",message
);