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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 #include "wine/debug.h"
31 #include "wscontrol.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(winsock
);
36 /* internal remapper function for the IP_ constants */
37 static INT
_remap_optname(INT level
, INT optname
)
39 TRACE("level=%d, optname=%d\n", level
, optname
);
40 if (level
== IPPROTO_IP
) {
41 switch (optname
) { /***** from value *****/
42 case 2: return 9; /* IP_MULTICAST_IF */
43 case 3: return 10; /* IP_MULTICAST_TTL */
44 case 4: return 11; /* IP_MULTICAST_LOOP */
45 case 5: return 12; /* IP_ADD_MEMBERSHIP */
46 case 6: return 13; /* IP_DROP_MEMBERSHIP */
47 case 7: return 4; /* IP_TTL */
48 case 8: return 3; /* IP_TOS */
49 case 9: return 14; /* IP_DONTFRAGMENT */
50 default: FIXME("Unknown optname %d, can't remap!\n", optname
); return optname
;
53 /* don't need to do anything */
58 /***********************************************************************
59 * setsockopt (WSOCK32.21)
61 * We have these forwarders because, for reasons unknown to us mere mortals,
62 * the values of the IP_ constants changed between winsock.h and winsock2.h.
63 * So, we need to remap them here.
65 INT WINAPI
WS1_setsockopt(SOCKET s
, INT level
, INT optname
, char *optval
, INT optlen
)
67 return setsockopt(s
, level
, _remap_optname(level
, optname
), optval
, optlen
);
70 /***********************************************************************
71 * getsockopt (WSOCK32.7)
73 INT WINAPI
WS1_getsockopt(SOCKET s
, INT level
, INT optname
, char *optval
, INT
*optlen
)
75 return getsockopt(s
, level
, _remap_optname(level
, optname
), optval
, optlen
);
78 /***********************************************************************
79 * WsControl (WSOCK32.1001)
81 * WsControl seems to be an undocumented Win95 function. A lot of
82 * discussion about WsControl can be found on the net, e.g.
83 * Subject: Re: WSOCK32.DLL WsControl Exported Function
84 * From: "Peter Rindfuss" <rindfuss-s@medea.wz-berlin.de>
87 * The WSCNTL_TCPIP_QUERY_INFO option is partially implemented based
88 * on observing the behaviour of WsControl with an app in
89 * Windows 98. It is not fully implemented, and there could
90 * be (are?) errors due to incorrect assumptions made.
93 * WsControl returns WSCTL_SUCCESS on success.
94 * ERROR_LOCK_VIOLATION is returned if the output buffer length
95 * (*pcbResponseInfoLen) is too small. This is an unusual error code, but
96 * it matches Win98's behavior. Other errors come from winerror.h, not from
97 * winsock.h. Again, this is to match Win98 behavior.
101 DWORD WINAPI
WsControl(DWORD protocol
,
104 LPDWORD pcbRequestInfoLen
,
105 LPVOID pResponseInfo
,
106 LPDWORD pcbResponseInfoLen
)
109 /* Get the command structure into a pointer we can use,
111 TDIObjectID
*pcommand
= (TDIObjectID
*)pRequestInfo
;
113 /* validate input parameters. Error codes are from winerror.h, not from
114 * winsock.h. pcbResponseInfoLen is apparently allowed to be NULL for some
115 * commands, since winipcfg.exe fails if we ensure it's non-NULL in every
118 if (protocol
!= IPPROTO_TCP
) return ERROR_INVALID_PARAMETER
;
119 if (!pcommand
) return ERROR_INVALID_PARAMETER
;
120 if (!pcbRequestInfoLen
) return ERROR_INVALID_ACCESS
;
121 if (*pcbRequestInfoLen
< sizeof(TDIObjectID
)) return ERROR_INVALID_ACCESS
;
122 if (!pResponseInfo
) return ERROR_INVALID_PARAMETER
;
123 if (pcommand
->toi_type
!= INFO_TYPE_PROVIDER
) return ERROR_INVALID_PARAMETER
;
125 TRACE (" WsControl TOI_ID=>0x%lx<, {TEI_ENTITY=0x%lx, TEI_INSTANCE=0x%lx}, TOI_CLASS=0x%lx, TOI_TYPE=0x%lx\n",
126 pcommand
->toi_id
, pcommand
->toi_entity
.tei_entity
,
127 pcommand
->toi_entity
.tei_instance
,
128 pcommand
->toi_class
, pcommand
->toi_type
);
132 case WSCNTL_TCPIP_QUERY_INFO
:
134 if (pcommand
->toi_class
!= INFO_CLASS_GENERIC
&&
135 pcommand
->toi_class
!= INFO_CLASS_PROTOCOL
)
137 ERR("Unexpected class %ld for WSCNTL_TCPIP_QUERY_INFO\n",
138 pcommand
->toi_class
);
139 return ERROR_BAD_ENVIRONMENT
;
142 switch (pcommand
->toi_id
)
144 /* ENTITY_LIST_ID gets the list of "entity IDs", where an entity
145 may represent an interface, or a datagram service, or address
146 translation, or other fun things. Typically an entity ID represents
147 a class of service, which is further queried for what type it is.
148 Different types will then have more specific queries defined.
152 TDIEntityID
*baseptr
= (TDIEntityID
*)pResponseInfo
;
153 DWORD numInt
, i
, ifTable
, spaceNeeded
;
156 if (!pcbResponseInfoLen
)
157 return ERROR_BAD_ENVIRONMENT
;
158 if (pcommand
->toi_class
!= INFO_CLASS_GENERIC
)
160 FIXME ("Unexpected Option for ENTITY_LIST_ID request -> toi_class=0x%lx\n",
161 pcommand
->toi_class
);
162 return (ERROR_BAD_ENVIRONMENT
);
165 GetNumberOfInterfaces(&numInt
);
166 spaceNeeded
= sizeof(TDIEntityID
) * (numInt
* 2 + 3);
168 if (*pcbResponseInfoLen
< spaceNeeded
)
169 return (ERROR_LOCK_VIOLATION
);
172 GetIfTable(NULL
, &ifTable
, FALSE
);
173 table
= (PMIB_IFTABLE
)calloc(1, ifTable
);
175 return ERROR_NOT_ENOUGH_MEMORY
;
176 GetIfTable(table
, &ifTable
, FALSE
);
178 spaceNeeded
= sizeof(TDIEntityID
) * (table
->dwNumEntries
+ 4);
179 if (*pcbResponseInfoLen
< spaceNeeded
)
182 return (ERROR_LOCK_VIOLATION
);
185 memset(baseptr
, 0, spaceNeeded
);
187 for (i
= 0; i
< table
->dwNumEntries
; i
++)
189 /* Return IF_GENERIC and CL_NL_ENTITY on every interface, and
190 * AT_ENTITY, CL_TL_ENTITY, and CO_TL_ENTITY on the first
191 * interface. MS returns them only on the loopback interface,
192 * but it doesn't seem to matter.
196 baseptr
->tei_entity
= CO_TL_ENTITY
;
197 baseptr
->tei_instance
= table
->table
[i
].dwIndex
;
199 baseptr
->tei_entity
= CL_TL_ENTITY
;
200 baseptr
->tei_instance
= table
->table
[i
].dwIndex
;
202 baseptr
->tei_entity
= AT_ENTITY
;
203 baseptr
->tei_instance
= table
->table
[i
].dwIndex
;
206 baseptr
->tei_entity
= CL_NL_ENTITY
;
207 baseptr
->tei_instance
= table
->table
[i
].dwIndex
;
209 baseptr
->tei_entity
= IF_GENERIC
;
210 baseptr
->tei_instance
= table
->table
[i
].dwIndex
;
214 *pcbResponseInfoLen
= spaceNeeded
;
220 /* Returns MIB-II statistics for an interface */
222 switch (pcommand
->toi_entity
.tei_entity
)
225 if (pcommand
->toi_class
== INFO_CLASS_GENERIC
)
227 if (!pcbResponseInfoLen
)
228 return ERROR_BAD_ENVIRONMENT
;
229 *((ULONG
*)pResponseInfo
) = IF_MIB
;
230 *pcbResponseInfoLen
= sizeof(ULONG
);
232 else if (pcommand
->toi_class
== INFO_CLASS_PROTOCOL
)
235 DWORD index
= pcommand
->toi_entity
.tei_instance
, ret
;
236 DWORD size
= sizeof(row
) - sizeof(row
.wszName
) -
239 if (!pcbResponseInfoLen
)
240 return ERROR_BAD_ENVIRONMENT
;
241 if (*pcbResponseInfoLen
< size
)
242 return (ERROR_LOCK_VIOLATION
);
244 ret
= GetIfEntry(&row
);
247 /* FIXME: Win98's arp.exe insists on querying index 1 for
248 * its MIB-II stats, regardless of the tei_instances
249 * returned in the ENTITY_LIST query above. If the query
250 * fails, arp.exe fails. So, I do this hack return value
251 * if index is 1 and the query failed just to get arp.exe
256 ERR ("Error retrieving data for interface index %lu\n",
260 size
= sizeof(row
) - sizeof(row
.wszName
) -
261 sizeof(row
.bDescr
) + row
.dwDescrLen
;
262 if (*pcbResponseInfoLen
< size
)
263 return (ERROR_LOCK_VIOLATION
);
264 memcpy(pResponseInfo
, &row
.dwIndex
, size
);
265 *pcbResponseInfoLen
= size
;
269 /* Returns address-translation related data. In our case, this is
273 if (pcommand
->toi_class
== INFO_CLASS_GENERIC
)
275 if (!pcbResponseInfoLen
)
276 return ERROR_BAD_ENVIRONMENT
;
277 *((ULONG
*)pResponseInfo
) = AT_ARP
;
278 *pcbResponseInfoLen
= sizeof(ULONG
);
280 else if (pcommand
->toi_class
== INFO_CLASS_PROTOCOL
)
282 PMIB_IPNETTABLE table
;
284 PULONG output
= (PULONG
)pResponseInfo
;
286 if (!pcbResponseInfoLen
)
287 return ERROR_BAD_ENVIRONMENT
;
288 if (*pcbResponseInfoLen
< sizeof(ULONG
) * 2)
289 return (ERROR_LOCK_VIOLATION
);
290 GetIpNetTable(NULL
, &size
, FALSE
);
291 table
= (PMIB_IPNETTABLE
)calloc(1, size
);
293 return ERROR_NOT_ENOUGH_MEMORY
;
294 GetIpNetTable(table
, &size
, FALSE
);
295 /* FIXME: I don't understand the meaning of the ARP output
296 * very well, but it seems to indicate how many ARP entries
297 * exist. I don't know whether this should reflect the
298 * number per interface, as I'm only testing with a single
299 * interface. So, I lie and say all ARP entries exist on
300 * a single interface--the first one that appears in the
303 *(output
++) = table
->dwNumEntries
;
304 *output
= table
->table
[0].dwIndex
;
306 *pcbResponseInfoLen
= sizeof(ULONG
) * 2;
310 /* Returns connectionless network layer statistics--in our case,
314 if (pcommand
->toi_class
== INFO_CLASS_GENERIC
)
316 if (!pcbResponseInfoLen
)
317 return ERROR_BAD_ENVIRONMENT
;
318 *((ULONG
*)pResponseInfo
) = CL_NL_IP
;
319 *pcbResponseInfoLen
= sizeof(ULONG
);
321 else if (pcommand
->toi_class
== INFO_CLASS_PROTOCOL
)
323 if (!pcbResponseInfoLen
)
324 return ERROR_BAD_ENVIRONMENT
;
325 if (*pcbResponseInfoLen
< sizeof(MIB_IPSTATS
))
326 return ERROR_LOCK_VIOLATION
;
327 GetIpStatistics((PMIB_IPSTATS
)pResponseInfo
);
329 *pcbResponseInfoLen
= sizeof(MIB_IPSTATS
);
333 /* Returns connectionless transport layer statistics--in our case,
337 if (pcommand
->toi_class
== INFO_CLASS_GENERIC
)
339 if (!pcbResponseInfoLen
)
340 return ERROR_BAD_ENVIRONMENT
;
341 *((ULONG
*)pResponseInfo
) = CL_TL_UDP
;
342 *pcbResponseInfoLen
= sizeof(ULONG
);
344 else if (pcommand
->toi_class
== INFO_CLASS_PROTOCOL
)
346 if (!pcbResponseInfoLen
)
347 return ERROR_BAD_ENVIRONMENT
;
348 if (*pcbResponseInfoLen
< sizeof(MIB_UDPSTATS
))
349 return ERROR_LOCK_VIOLATION
;
350 GetUdpStatistics((PMIB_UDPSTATS
)pResponseInfo
);
351 *pcbResponseInfoLen
= sizeof(MIB_UDPSTATS
);
355 /* Returns connection-oriented transport layer statistics--in our
359 if (pcommand
->toi_class
== INFO_CLASS_GENERIC
)
361 if (!pcbResponseInfoLen
)
362 return ERROR_BAD_ENVIRONMENT
;
363 *((ULONG
*)pResponseInfo
) = CO_TL_TCP
;
364 *pcbResponseInfoLen
= sizeof(ULONG
);
366 else if (pcommand
->toi_class
== INFO_CLASS_PROTOCOL
)
368 if (!pcbResponseInfoLen
)
369 return ERROR_BAD_ENVIRONMENT
;
370 if (*pcbResponseInfoLen
< sizeof(MIB_TCPSTATS
))
371 return ERROR_LOCK_VIOLATION
;
372 GetTcpStatistics((PMIB_TCPSTATS
)pResponseInfo
);
373 *pcbResponseInfoLen
= sizeof(MIB_TCPSTATS
);
378 ERR("Unknown entity %ld for ENTITY_TYPE_ID query\n",
379 pcommand
->toi_entity
.tei_entity
);
383 /* This call returns the IP address, subnet mask, and broadcast
384 * address for an interface. If there are multiple IP addresses for
385 * the interface with the given index, returns the "first" one.
387 case IP_MIB_ADDRTABLE_ENTRY_ID
:
389 DWORD index
= pcommand
->toi_entity
.tei_instance
;
390 PMIB_IPADDRROW baseIPInfo
= (PMIB_IPADDRROW
) pResponseInfo
;
391 PMIB_IPADDRTABLE table
;
394 if (!pcbResponseInfoLen
)
395 return ERROR_BAD_ENVIRONMENT
;
396 if (*pcbResponseInfoLen
< sizeof(MIB_IPADDRROW
))
397 return (ERROR_LOCK_VIOLATION
);
399 /* get entire table, because there isn't an exported function that
400 gets just one entry. */
402 GetIpAddrTable(NULL
, &tableSize
, FALSE
);
403 table
= (PMIB_IPADDRTABLE
)calloc(1, tableSize
);
405 return ERROR_NOT_ENOUGH_MEMORY
;
406 GetIpAddrTable(table
, &tableSize
, FALSE
);
407 for (i
= 0; i
< table
->dwNumEntries
; i
++)
409 if (table
->table
[i
].dwIndex
== index
)
411 TRACE("Found IP info for tei_instance 0x%lx:\n", index
);
412 TRACE("IP 0x%08lx, mask 0x%08lx\n", table
->table
[i
].dwAddr
,
413 table
->table
[i
].dwMask
);
414 memcpy(baseIPInfo
, &table
->table
[i
], sizeof(MIB_IPADDRROW
));
420 *pcbResponseInfoLen
= sizeof(MIB_IPADDRROW
);
424 case IP_MIB_TABLE_ENTRY_ID
:
426 switch (pcommand
->toi_entity
.tei_entity
)
428 /* This call returns the routing table.
429 * No official documentation found, even the name of the command is unknown.
431 * http://www.cyberport.com/~tangent/programming/winsock/articles/wscontrol.html
432 * and testings done with winipcfg.exe, route.exe and ipconfig.exe.
433 * pcommand->toi_entity.tei_instance seems to be the interface number
434 * but route.exe outputs only the information for the last interface
435 * if only the routes for the pcommand->toi_entity.tei_instance
436 * interface are returned. */
439 DWORD routeTableSize
, numRoutes
, ndx
;
440 PMIB_IPFORWARDTABLE table
;
441 IPRouteEntry
*winRouteTable
= (IPRouteEntry
*) pResponseInfo
;
443 if (!pcbResponseInfoLen
)
444 return ERROR_BAD_ENVIRONMENT
;
445 GetIpForwardTable(NULL
, &routeTableSize
, FALSE
);
446 numRoutes
= min(routeTableSize
- sizeof(MIB_IPFORWARDTABLE
),
447 0) / sizeof(MIB_IPFORWARDROW
) + 1;
448 if (*pcbResponseInfoLen
< sizeof(IPRouteEntry
) * numRoutes
)
449 return (ERROR_LOCK_VIOLATION
);
450 table
= (PMIB_IPFORWARDTABLE
)calloc(1, routeTableSize
);
452 return ERROR_NOT_ENOUGH_MEMORY
;
453 GetIpForwardTable(table
, &routeTableSize
, FALSE
);
455 memset(pResponseInfo
, 0, sizeof(IPRouteEntry
) * numRoutes
);
456 for (ndx
= 0; ndx
< table
->dwNumEntries
; ndx
++)
458 winRouteTable
->ire_addr
= table
->table
[ndx
].dwForwardDest
;
459 winRouteTable
->ire_index
=
460 table
->table
[ndx
].dwForwardIfIndex
;
461 winRouteTable
->ire_metric
=
462 table
->table
[ndx
].dwForwardMetric1
;
463 /* winRouteTable->ire_option4 =
464 winRouteTable->ire_option5 =
465 winRouteTable->ire_option6 = */
466 winRouteTable
->ire_gw
= table
->table
[ndx
].dwForwardNextHop
;
467 /* winRouteTable->ire_option8 =
468 winRouteTable->ire_option9 =
469 winRouteTable->ire_option10 = */
470 winRouteTable
->ire_mask
= table
->table
[ndx
].dwForwardMask
;
471 /* winRouteTable->ire_option12 = */
475 /* calculate the length of the data in the output buffer */
476 *pcbResponseInfoLen
= sizeof(IPRouteEntry
) *
485 DWORD arpTableSize
, numEntries
, ret
;
486 PMIB_IPNETTABLE table
;
488 if (!pcbResponseInfoLen
)
489 return ERROR_BAD_ENVIRONMENT
;
490 GetIpNetTable(NULL
, &arpTableSize
, FALSE
);
491 numEntries
= min(arpTableSize
- sizeof(MIB_IPNETTABLE
),
492 0) / sizeof(MIB_IPNETROW
) + 1;
493 if (*pcbResponseInfoLen
< sizeof(MIB_IPNETROW
) * numEntries
)
494 return (ERROR_LOCK_VIOLATION
);
495 table
= (PMIB_IPNETTABLE
)calloc(1, arpTableSize
);
497 return ERROR_NOT_ENOUGH_MEMORY
;
498 ret
= GetIpNetTable(table
, &arpTableSize
, FALSE
);
501 if (*pcbResponseInfoLen
< sizeof(MIB_IPNETROW
) *
505 return ERROR_LOCK_VIOLATION
;
507 memcpy(pResponseInfo
, table
->table
, sizeof(MIB_IPNETROW
) *
508 table
->dwNumEntries
);
510 /* calculate the length of the data in the output buffer */
511 *pcbResponseInfoLen
= sizeof(MIB_IPNETROW
) *
520 DWORD tcpTableSize
, numEntries
, ret
;
524 if (!pcbResponseInfoLen
)
525 return ERROR_BAD_ENVIRONMENT
;
526 GetTcpTable(NULL
, &tcpTableSize
, FALSE
);
527 numEntries
= min(tcpTableSize
- sizeof(MIB_TCPTABLE
),
528 0) / sizeof(MIB_TCPROW
) + 1;
529 if (*pcbResponseInfoLen
< sizeof(MIB_TCPROW
) * numEntries
)
530 return (ERROR_LOCK_VIOLATION
);
531 table
= (PMIB_TCPTABLE
)calloc(1, tcpTableSize
);
533 return ERROR_NOT_ENOUGH_MEMORY
;
534 ret
= GetTcpTable(table
, &tcpTableSize
, FALSE
);
537 if (*pcbResponseInfoLen
< sizeof(MIB_TCPROW
) *
541 return ERROR_LOCK_VIOLATION
;
543 for (i
= 0; i
< table
->dwNumEntries
; i
++)
547 sPort
= ntohs((USHORT
)table
->table
[i
].dwLocalPort
);
548 table
->table
[i
].dwLocalPort
= (DWORD
)sPort
;
549 sPort
= ntohs((USHORT
)table
->table
[i
].dwRemotePort
);
550 table
->table
[i
].dwRemotePort
= (DWORD
)sPort
;
552 memcpy(pResponseInfo
, table
->table
, sizeof(MIB_TCPROW
) *
553 table
->dwNumEntries
);
555 /* calculate the length of the data in the output buffer */
556 *pcbResponseInfoLen
= sizeof(MIB_TCPROW
) *
565 FIXME ("Command ID Not Supported -> toi_id=0x%lx, toi_entity={tei_entity=0x%lx, tei_instance=0x%lx}, toi_class=0x%lx\n",
566 pcommand
->toi_id
, pcommand
->toi_entity
.tei_entity
,
567 pcommand
->toi_entity
.tei_instance
, pcommand
->toi_class
);
569 return (ERROR_BAD_ENVIRONMENT
);
578 FIXME ("Command ID Not Supported -> toi_id=0x%lx, toi_entity={tei_entity=0x%lx, tei_instance=0x%lx}, toi_class=0x%lx\n",
579 pcommand
->toi_id
, pcommand
->toi_entity
.tei_entity
,
580 pcommand
->toi_entity
.tei_instance
, pcommand
->toi_class
);
582 return (ERROR_BAD_ENVIRONMENT
);
589 case WSCNTL_TCPIP_ICMP_ECHO
:
591 unsigned int addr
= *(unsigned int*)pRequestInfo
;
593 int timeout
= *(unsigned int*)(inbuf
+4);
594 short x1
= *(unsigned short*)(inbuf
+8);
595 short sendbufsize
= *(unsigned short*)(inbuf
+10);
596 char x2
= *(unsigned char*)(inbuf
+12);
597 char ttl
= *(unsigned char*)(inbuf
+13);
598 char service
= *(unsigned char*)(inbuf
+14);
599 char type
= *(unsigned char*)(inbuf
+15); /* 0x2: don't fragment*/
602 FIXME("(ICMP_ECHO) to 0x%08x stub \n", addr
);
607 FIXME("Protocol Not Supported -> protocol=0x%lx, action=0x%lx, Request=%p, RequestLen=%p, Response=%p, ResponseLen=%p\n",
608 protocol
, action
, pRequestInfo
, pcbRequestInfoLen
, pResponseInfo
, pcbResponseInfoLen
);
610 return (WSAEOPNOTSUPP
);
614 return (WSCTL_SUCCESS
);
619 /***********************************************************************
620 * WSARecvEx (WSOCK32.1107)
622 * WSARecvEx is a Microsoft specific extension to winsock that is identical to recv
623 * except that has an in/out argument call flags that has the value MSG_PARTIAL ored
624 * into the flags parameter when a partial packet is read. This only applies to
625 * sockets using the datagram protocol. This method does not seem to be implemented
626 * correctly by microsoft as the winsock implementation does not set the MSG_PARTIAL
627 * flag when a fragmented packet arrives.
629 INT WINAPI
WSARecvEx(SOCKET s
, char *buf
, INT len
, INT
*flags
)
631 FIXME("(WSARecvEx) partial packet return value not set \n");
632 return recv(s
, buf
, len
, *flags
);
636 /***********************************************************************
637 * s_perror (WSOCK32.1108)
639 void WINAPI
s_perror(LPCSTR message
)
641 FIXME("(%s): stub\n",message
);