mirror of
git://source.winehq.org/git/wine.git
synced 2024-09-02 20:33:38 +00:00
657 lines
26 KiB
C
657 lines
26 KiB
C
/*
|
|
* WSOCK32 specific functions
|
|
*
|
|
* Copyright (C) 1993,1994,1996,1997 John Brezak, Erik Bos, Alex Korobka.
|
|
* Copyright (C) 2003 Juan Lang.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
|
|
#include "config.h"
|
|
#include <stdarg.h>
|
|
|
|
#include "windef.h"
|
|
#include "winbase.h"
|
|
#include "wine/debug.h"
|
|
#include "winsock2.h"
|
|
#include "winnt.h"
|
|
#include "wscontrol.h"
|
|
#include "iphlpapi.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(winsock);
|
|
|
|
/* internal remapper function for the IP_ constants */
|
|
static INT _remap_optname(INT level, INT optname)
|
|
{
|
|
TRACE("level=%d, optname=%d\n", level, optname);
|
|
if (level == IPPROTO_IP) {
|
|
switch (optname) { /***** from value *****/
|
|
case 2: return 9; /* IP_MULTICAST_IF */
|
|
case 3: return 10; /* IP_MULTICAST_TTL */
|
|
case 4: return 11; /* IP_MULTICAST_LOOP */
|
|
case 5: return 12; /* IP_ADD_MEMBERSHIP */
|
|
case 6: return 13; /* IP_DROP_MEMBERSHIP */
|
|
case 7: return 4; /* IP_TTL */
|
|
case 8: return 3; /* IP_TOS */
|
|
case 9: return 14; /* IP_DONTFRAGMENT */
|
|
default: FIXME("Unknown optname %d, can't remap!\n", optname); return optname;
|
|
}
|
|
} else {
|
|
/* don't need to do anything */
|
|
return optname;
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
* setsockopt (WSOCK32.21)
|
|
*
|
|
* We have these forwarders because, for reasons unknown to us mere mortals,
|
|
* the values of the IP_ constants changed between winsock.h and winsock2.h.
|
|
* So, we need to remap them here.
|
|
*/
|
|
INT WINAPI WS1_setsockopt(SOCKET s, INT level, INT optname, char *optval, INT optlen)
|
|
{
|
|
return setsockopt(s, level, _remap_optname(level, optname), optval, optlen);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* getsockopt (WSOCK32.7)
|
|
*/
|
|
INT WINAPI WS1_getsockopt(SOCKET s, INT level, INT optname, char *optval, INT *optlen)
|
|
{
|
|
return getsockopt(s, level, _remap_optname(level, optname), optval, optlen);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WsControl (WSOCK32.1001)
|
|
*
|
|
* WsControl seems to be an undocumented Win95 function. A lot of
|
|
* discussion about WsControl can be found on the net, e.g.
|
|
* Subject: Re: WSOCK32.DLL WsControl Exported Function
|
|
* From: "Peter Rindfuss" <rindfuss-s@medea.wz-berlin.de>
|
|
* Date: 1997/08/17
|
|
*
|
|
* The WSCNTL_TCPIP_QUERY_INFO option is partially implemented based
|
|
* on observing the behaviour of WsControl with an app in
|
|
* Windows 98. It is not fully implemented, and there could
|
|
* be (are?) errors due to incorrect assumptions made.
|
|
*
|
|
*
|
|
* WsControl returns WSCTL_SUCCESS on success.
|
|
* ERROR_LOCK_VIOLATION is returned if the output buffer length
|
|
* (*pcbResponseInfoLen) is too small. This is an unusual error code, but
|
|
* it matches Win98's behavior. Other errors come from winerror.h, not from
|
|
* winsock.h. Again, this is to match Win98 behavior.
|
|
*
|
|
*/
|
|
|
|
DWORD WINAPI WsControl(DWORD protocol,
|
|
DWORD action,
|
|
LPVOID pRequestInfo,
|
|
LPDWORD pcbRequestInfoLen,
|
|
LPVOID pResponseInfo,
|
|
LPDWORD pcbResponseInfoLen)
|
|
{
|
|
|
|
/* Get the command structure into a pointer we can use,
|
|
rather than void */
|
|
TDIObjectID *pcommand = pRequestInfo;
|
|
|
|
/* validate input parameters. Error codes are from winerror.h, not from
|
|
* winsock.h. pcbResponseInfoLen is apparently allowed to be NULL for some
|
|
* commands, since winipcfg.exe fails if we ensure it's non-NULL in every
|
|
* case.
|
|
*/
|
|
if (protocol != IPPROTO_TCP) return ERROR_INVALID_PARAMETER;
|
|
if (!pcommand) return ERROR_INVALID_PARAMETER;
|
|
if (!pcbRequestInfoLen) return ERROR_INVALID_ACCESS;
|
|
if (*pcbRequestInfoLen < sizeof(TDIObjectID)) return ERROR_INVALID_ACCESS;
|
|
if (!pResponseInfo) return ERROR_INVALID_PARAMETER;
|
|
if (pcommand->toi_type != INFO_TYPE_PROVIDER) return ERROR_INVALID_PARAMETER;
|
|
|
|
TRACE (" WsControl TOI_ID=>0x%lx<, {TEI_ENTITY=0x%lx, TEI_INSTANCE=0x%lx}, TOI_CLASS=0x%lx, TOI_TYPE=0x%lx\n",
|
|
pcommand->toi_id, pcommand->toi_entity.tei_entity,
|
|
pcommand->toi_entity.tei_instance,
|
|
pcommand->toi_class, pcommand->toi_type );
|
|
|
|
switch (action)
|
|
{
|
|
case WSCNTL_TCPIP_QUERY_INFO:
|
|
{
|
|
if (pcommand->toi_class != INFO_CLASS_GENERIC &&
|
|
pcommand->toi_class != INFO_CLASS_PROTOCOL)
|
|
{
|
|
ERR("Unexpected class %ld for WSCNTL_TCPIP_QUERY_INFO\n",
|
|
pcommand->toi_class);
|
|
return ERROR_BAD_ENVIRONMENT;
|
|
}
|
|
|
|
switch (pcommand->toi_id)
|
|
{
|
|
/* ENTITY_LIST_ID gets the list of "entity IDs", where an entity
|
|
may represent an interface, or a datagram service, or address
|
|
translation, or other fun things. Typically an entity ID represents
|
|
a class of service, which is further queried for what type it is.
|
|
Different types will then have more specific queries defined.
|
|
*/
|
|
case ENTITY_LIST_ID:
|
|
{
|
|
TDIEntityID *baseptr = pResponseInfo;
|
|
DWORD numInt, i, ifTable, spaceNeeded;
|
|
PMIB_IFTABLE table;
|
|
|
|
if (!pcbResponseInfoLen)
|
|
return ERROR_BAD_ENVIRONMENT;
|
|
if (pcommand->toi_class != INFO_CLASS_GENERIC)
|
|
{
|
|
FIXME ("Unexpected Option for ENTITY_LIST_ID request -> toi_class=0x%lx\n",
|
|
pcommand->toi_class);
|
|
return (ERROR_BAD_ENVIRONMENT);
|
|
}
|
|
|
|
GetNumberOfInterfaces(&numInt);
|
|
spaceNeeded = sizeof(TDIEntityID) * (numInt * 2 + 3);
|
|
|
|
if (*pcbResponseInfoLen < spaceNeeded)
|
|
return (ERROR_LOCK_VIOLATION);
|
|
|
|
ifTable = 0;
|
|
GetIfTable(NULL, &ifTable, FALSE);
|
|
table = HeapAlloc( GetProcessHeap(), 0, ifTable );
|
|
if (!table)
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
GetIfTable(table, &ifTable, FALSE);
|
|
|
|
spaceNeeded = sizeof(TDIEntityID) * (table->dwNumEntries + 4);
|
|
if (*pcbResponseInfoLen < spaceNeeded)
|
|
{
|
|
HeapFree( GetProcessHeap(), 0, table );
|
|
return ERROR_LOCK_VIOLATION;
|
|
}
|
|
|
|
memset(baseptr, 0, spaceNeeded);
|
|
|
|
for (i = 0; i < table->dwNumEntries; i++)
|
|
{
|
|
/* Return IF_GENERIC and CL_NL_ENTITY on every interface, and
|
|
* AT_ENTITY, CL_TL_ENTITY, and CO_TL_ENTITY on the first
|
|
* interface. MS returns them only on the loopback interface,
|
|
* but it doesn't seem to matter.
|
|
*/
|
|
if (i == 0)
|
|
{
|
|
baseptr->tei_entity = CO_TL_ENTITY;
|
|
baseptr->tei_instance = table->table[i].dwIndex;
|
|
baseptr++;
|
|
baseptr->tei_entity = CL_TL_ENTITY;
|
|
baseptr->tei_instance = table->table[i].dwIndex;
|
|
baseptr++;
|
|
baseptr->tei_entity = AT_ENTITY;
|
|
baseptr->tei_instance = table->table[i].dwIndex;
|
|
baseptr++;
|
|
}
|
|
baseptr->tei_entity = CL_NL_ENTITY;
|
|
baseptr->tei_instance = table->table[i].dwIndex;
|
|
baseptr++;
|
|
baseptr->tei_entity = IF_GENERIC;
|
|
baseptr->tei_instance = table->table[i].dwIndex;
|
|
baseptr++;
|
|
}
|
|
|
|
*pcbResponseInfoLen = spaceNeeded;
|
|
HeapFree( GetProcessHeap(), 0, table );
|
|
break;
|
|
}
|
|
|
|
/* Returns MIB-II statistics for an interface */
|
|
case ENTITY_TYPE_ID:
|
|
switch (pcommand->toi_entity.tei_entity)
|
|
{
|
|
case IF_GENERIC:
|
|
if (pcommand->toi_class == INFO_CLASS_GENERIC)
|
|
{
|
|
if (!pcbResponseInfoLen)
|
|
return ERROR_BAD_ENVIRONMENT;
|
|
*((ULONG *)pResponseInfo) = IF_MIB;
|
|
*pcbResponseInfoLen = sizeof(ULONG);
|
|
}
|
|
else if (pcommand->toi_class == INFO_CLASS_PROTOCOL)
|
|
{
|
|
MIB_IFROW row;
|
|
DWORD index = pcommand->toi_entity.tei_instance, ret;
|
|
DWORD size = sizeof(row) - sizeof(row.wszName) -
|
|
sizeof(row.bDescr);
|
|
|
|
if (!pcbResponseInfoLen)
|
|
return ERROR_BAD_ENVIRONMENT;
|
|
if (*pcbResponseInfoLen < size)
|
|
return (ERROR_LOCK_VIOLATION);
|
|
row.dwIndex = index;
|
|
ret = GetIfEntry(&row);
|
|
if (ret != NO_ERROR)
|
|
{
|
|
/* FIXME: Win98's arp.exe insists on querying index 1 for
|
|
* its MIB-II stats, regardless of the tei_instances
|
|
* returned in the ENTITY_LIST query above. If the query
|
|
* fails, arp.exe fails. So, I do this hack return value
|
|
* if index is 1 and the query failed just to get arp.exe
|
|
* to continue.
|
|
*/
|
|
if (index == 1)
|
|
return NO_ERROR;
|
|
ERR ("Error retrieving data for interface index %u\n",
|
|
index);
|
|
return ret;
|
|
}
|
|
size = sizeof(row) - sizeof(row.wszName) -
|
|
sizeof(row.bDescr) + row.dwDescrLen;
|
|
if (*pcbResponseInfoLen < size)
|
|
return (ERROR_LOCK_VIOLATION);
|
|
memcpy(pResponseInfo, &row.dwIndex, size);
|
|
*pcbResponseInfoLen = size;
|
|
}
|
|
break;
|
|
|
|
/* Returns address-translation related data. In our case, this is
|
|
* ARP.
|
|
*/
|
|
case AT_ENTITY:
|
|
if (pcommand->toi_class == INFO_CLASS_GENERIC)
|
|
{
|
|
if (!pcbResponseInfoLen)
|
|
return ERROR_BAD_ENVIRONMENT;
|
|
*((ULONG *)pResponseInfo) = AT_ARP;
|
|
*pcbResponseInfoLen = sizeof(ULONG);
|
|
}
|
|
else if (pcommand->toi_class == INFO_CLASS_PROTOCOL)
|
|
{
|
|
PMIB_IPNETTABLE table;
|
|
DWORD size;
|
|
PULONG output = pResponseInfo;
|
|
|
|
if (!pcbResponseInfoLen)
|
|
return ERROR_BAD_ENVIRONMENT;
|
|
if (*pcbResponseInfoLen < sizeof(ULONG) * 2)
|
|
return (ERROR_LOCK_VIOLATION);
|
|
GetIpNetTable(NULL, &size, FALSE);
|
|
table = HeapAlloc( GetProcessHeap(), 0, size );
|
|
if (!table)
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
GetIpNetTable(table, &size, FALSE);
|
|
/* FIXME: I don't understand the meaning of the ARP output
|
|
* very well, but it seems to indicate how many ARP entries
|
|
* exist. I don't know whether this should reflect the
|
|
* number per interface, as I'm only testing with a single
|
|
* interface. So, I lie and say all ARP entries exist on
|
|
* a single interface--the first one that appears in the
|
|
* ARP table.
|
|
*/
|
|
*(output++) = table->dwNumEntries;
|
|
*output = table->table[0].dwIndex;
|
|
HeapFree( GetProcessHeap(), 0, table );
|
|
*pcbResponseInfoLen = sizeof(ULONG) * 2;
|
|
}
|
|
break;
|
|
|
|
/* Returns connectionless network layer statistics--in our case,
|
|
* this is IP.
|
|
*/
|
|
case CL_NL_ENTITY:
|
|
if (pcommand->toi_class == INFO_CLASS_GENERIC)
|
|
{
|
|
if (!pcbResponseInfoLen)
|
|
return ERROR_BAD_ENVIRONMENT;
|
|
*((ULONG *)pResponseInfo) = CL_NL_IP;
|
|
*pcbResponseInfoLen = sizeof(ULONG);
|
|
}
|
|
else if (pcommand->toi_class == INFO_CLASS_PROTOCOL)
|
|
{
|
|
if (!pcbResponseInfoLen)
|
|
return ERROR_BAD_ENVIRONMENT;
|
|
if (*pcbResponseInfoLen < sizeof(MIB_IPSTATS))
|
|
return ERROR_LOCK_VIOLATION;
|
|
GetIpStatistics(pResponseInfo);
|
|
|
|
*pcbResponseInfoLen = sizeof(MIB_IPSTATS);
|
|
}
|
|
break;
|
|
|
|
/* Returns connectionless transport layer statistics--in our case,
|
|
* this is UDP.
|
|
*/
|
|
case CL_TL_ENTITY:
|
|
if (pcommand->toi_class == INFO_CLASS_GENERIC)
|
|
{
|
|
if (!pcbResponseInfoLen)
|
|
return ERROR_BAD_ENVIRONMENT;
|
|
*((ULONG *)pResponseInfo) = CL_TL_UDP;
|
|
*pcbResponseInfoLen = sizeof(ULONG);
|
|
}
|
|
else if (pcommand->toi_class == INFO_CLASS_PROTOCOL)
|
|
{
|
|
if (!pcbResponseInfoLen)
|
|
return ERROR_BAD_ENVIRONMENT;
|
|
if (*pcbResponseInfoLen < sizeof(MIB_UDPSTATS))
|
|
return ERROR_LOCK_VIOLATION;
|
|
GetUdpStatistics(pResponseInfo);
|
|
*pcbResponseInfoLen = sizeof(MIB_UDPSTATS);
|
|
}
|
|
break;
|
|
|
|
/* Returns connection-oriented transport layer statistics--in our
|
|
* case, this is TCP.
|
|
*/
|
|
case CO_TL_ENTITY:
|
|
if (pcommand->toi_class == INFO_CLASS_GENERIC)
|
|
{
|
|
if (!pcbResponseInfoLen)
|
|
return ERROR_BAD_ENVIRONMENT;
|
|
*((ULONG *)pResponseInfo) = CO_TL_TCP;
|
|
*pcbResponseInfoLen = sizeof(ULONG);
|
|
}
|
|
else if (pcommand->toi_class == INFO_CLASS_PROTOCOL)
|
|
{
|
|
if (!pcbResponseInfoLen)
|
|
return ERROR_BAD_ENVIRONMENT;
|
|
if (*pcbResponseInfoLen < sizeof(MIB_TCPSTATS))
|
|
return ERROR_LOCK_VIOLATION;
|
|
GetTcpStatistics(pResponseInfo);
|
|
*pcbResponseInfoLen = sizeof(MIB_TCPSTATS);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ERR("Unknown entity %ld for ENTITY_TYPE_ID query\n",
|
|
pcommand->toi_entity.tei_entity);
|
|
}
|
|
break;
|
|
|
|
/* This call returns the IP address, subnet mask, and broadcast
|
|
* address for an interface. If there are multiple IP addresses for
|
|
* the interface with the given index, returns the "first" one.
|
|
*/
|
|
case IP_MIB_ADDRTABLE_ENTRY_ID:
|
|
{
|
|
DWORD index = pcommand->toi_entity.tei_instance;
|
|
PMIB_IPADDRROW baseIPInfo = pResponseInfo;
|
|
PMIB_IPADDRTABLE table;
|
|
DWORD tableSize, i;
|
|
|
|
if (!pcbResponseInfoLen)
|
|
return ERROR_BAD_ENVIRONMENT;
|
|
if (*pcbResponseInfoLen < sizeof(MIB_IPADDRROW))
|
|
return (ERROR_LOCK_VIOLATION);
|
|
|
|
/* get entire table, because there isn't an exported function that
|
|
gets just one entry. */
|
|
tableSize = 0;
|
|
GetIpAddrTable(NULL, &tableSize, FALSE);
|
|
table = HeapAlloc( GetProcessHeap(), 0, tableSize );
|
|
if (!table)
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
GetIpAddrTable(table, &tableSize, FALSE);
|
|
for (i = 0; i < table->dwNumEntries; i++)
|
|
{
|
|
if (table->table[i].dwIndex == index)
|
|
{
|
|
TRACE("Found IP info for tei_instance 0x%x:\n", index);
|
|
TRACE("IP 0x%08x, mask 0x%08x\n", table->table[i].dwAddr,
|
|
table->table[i].dwMask);
|
|
*baseIPInfo = table->table[i];
|
|
break;
|
|
}
|
|
}
|
|
HeapFree( GetProcessHeap(), 0, table );
|
|
|
|
*pcbResponseInfoLen = sizeof(MIB_IPADDRROW);
|
|
break;
|
|
}
|
|
|
|
case IP_MIB_TABLE_ENTRY_ID:
|
|
{
|
|
switch (pcommand->toi_entity.tei_entity)
|
|
{
|
|
/* This call returns the routing table.
|
|
* No official documentation found, even the name of the command is unknown.
|
|
* Work is based on
|
|
* http://www.cyberport.com/~tangent/programming/winsock/articles/wscontrol.html
|
|
* and testings done with winipcfg.exe, route.exe and ipconfig.exe.
|
|
* pcommand->toi_entity.tei_instance seems to be the interface number
|
|
* but route.exe outputs only the information for the last interface
|
|
* if only the routes for the pcommand->toi_entity.tei_instance
|
|
* interface are returned. */
|
|
case CL_NL_ENTITY:
|
|
{
|
|
DWORD routeTableSize, numRoutes, ndx, ret;
|
|
PMIB_IPFORWARDTABLE table;
|
|
IPRouteEntry *winRouteTable = pResponseInfo;
|
|
|
|
if (!pcbResponseInfoLen)
|
|
return ERROR_BAD_ENVIRONMENT;
|
|
ret = GetIpForwardTable(NULL, &routeTableSize, FALSE);
|
|
if (ret != ERROR_INSUFFICIENT_BUFFER)
|
|
return ret;
|
|
numRoutes = (routeTableSize - sizeof(MIB_IPFORWARDTABLE))
|
|
/ sizeof(MIB_IPFORWARDROW) + 1;
|
|
if (*pcbResponseInfoLen < sizeof(IPRouteEntry) * numRoutes)
|
|
return (ERROR_LOCK_VIOLATION);
|
|
table = HeapAlloc( GetProcessHeap(), 0, routeTableSize );
|
|
if (!table)
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
ret = GetIpForwardTable(table, &routeTableSize, FALSE);
|
|
if (ret != NO_ERROR) {
|
|
HeapFree( GetProcessHeap(), 0, table );
|
|
return ret;
|
|
}
|
|
|
|
memset(pResponseInfo, 0, sizeof(IPRouteEntry) * numRoutes);
|
|
for (ndx = 0; ndx < table->dwNumEntries; ndx++)
|
|
{
|
|
winRouteTable->ire_addr = table->table[ndx].dwForwardDest;
|
|
winRouteTable->ire_index =
|
|
table->table[ndx].dwForwardIfIndex;
|
|
winRouteTable->ire_metric =
|
|
table->table[ndx].dwForwardMetric1;
|
|
/* winRouteTable->ire_option4 =
|
|
winRouteTable->ire_option5 =
|
|
winRouteTable->ire_option6 = */
|
|
winRouteTable->ire_gw = table->table[ndx].dwForwardNextHop;
|
|
/* winRouteTable->ire_option8 =
|
|
winRouteTable->ire_option9 =
|
|
winRouteTable->ire_option10 = */
|
|
winRouteTable->ire_mask = table->table[ndx].dwForwardMask;
|
|
/* winRouteTable->ire_option12 = */
|
|
winRouteTable++;
|
|
}
|
|
|
|
/* calculate the length of the data in the output buffer */
|
|
*pcbResponseInfoLen = sizeof(IPRouteEntry) *
|
|
table->dwNumEntries;
|
|
|
|
HeapFree( GetProcessHeap(), 0, table );
|
|
}
|
|
break;
|
|
|
|
case AT_ARP:
|
|
{
|
|
DWORD arpTableSize, numEntries, ret;
|
|
PMIB_IPNETTABLE table;
|
|
|
|
if (!pcbResponseInfoLen)
|
|
return ERROR_BAD_ENVIRONMENT;
|
|
ret = GetIpNetTable(NULL, &arpTableSize, FALSE);
|
|
if (ret != ERROR_INSUFFICIENT_BUFFER)
|
|
return ret;
|
|
numEntries = (arpTableSize - sizeof(MIB_IPNETTABLE))
|
|
/ sizeof(MIB_IPNETROW) + 1;
|
|
if (*pcbResponseInfoLen < sizeof(MIB_IPNETROW) * numEntries)
|
|
return (ERROR_LOCK_VIOLATION);
|
|
table = HeapAlloc( GetProcessHeap(), 0, arpTableSize );
|
|
if (!table)
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
ret = GetIpNetTable(table, &arpTableSize, FALSE);
|
|
if (ret != NO_ERROR) {
|
|
HeapFree( GetProcessHeap(), 0, table );
|
|
return ret;
|
|
}
|
|
if (*pcbResponseInfoLen < sizeof(MIB_IPNETROW) *
|
|
table->dwNumEntries)
|
|
{
|
|
HeapFree( GetProcessHeap(), 0, table );
|
|
return ERROR_LOCK_VIOLATION;
|
|
}
|
|
memcpy(pResponseInfo, table->table, sizeof(MIB_IPNETROW) *
|
|
table->dwNumEntries);
|
|
|
|
/* calculate the length of the data in the output buffer */
|
|
*pcbResponseInfoLen = sizeof(MIB_IPNETROW) *
|
|
table->dwNumEntries;
|
|
|
|
HeapFree( GetProcessHeap(), 0, table );
|
|
}
|
|
break;
|
|
|
|
case CO_TL_ENTITY:
|
|
{
|
|
DWORD tcpTableSize, numEntries, ret;
|
|
PMIB_TCPTABLE table;
|
|
DWORD i;
|
|
|
|
if (!pcbResponseInfoLen)
|
|
return ERROR_BAD_ENVIRONMENT;
|
|
ret = GetTcpTable(NULL, &tcpTableSize, FALSE);
|
|
if (ret != ERROR_INSUFFICIENT_BUFFER)
|
|
return ret;
|
|
numEntries = (tcpTableSize - sizeof(MIB_TCPTABLE))
|
|
/ sizeof(MIB_TCPROW) + 1;
|
|
if (*pcbResponseInfoLen < sizeof(MIB_TCPROW) * numEntries)
|
|
return (ERROR_LOCK_VIOLATION);
|
|
table = HeapAlloc( GetProcessHeap(), 0, tcpTableSize );
|
|
if (!table)
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
ret = GetTcpTable(table, &tcpTableSize, FALSE);
|
|
if (ret != NO_ERROR) {
|
|
HeapFree( GetProcessHeap(), 0, table );
|
|
return ret;
|
|
}
|
|
if (*pcbResponseInfoLen < sizeof(MIB_TCPROW) *
|
|
table->dwNumEntries)
|
|
{
|
|
HeapFree( GetProcessHeap(), 0, table );
|
|
return ERROR_LOCK_VIOLATION;
|
|
}
|
|
for (i = 0; i < table->dwNumEntries; i++)
|
|
{
|
|
USHORT sPort;
|
|
|
|
sPort = ntohs((USHORT)table->table[i].dwLocalPort);
|
|
table->table[i].dwLocalPort = (DWORD)sPort;
|
|
sPort = ntohs((USHORT)table->table[i].dwRemotePort);
|
|
table->table[i].dwRemotePort = (DWORD)sPort;
|
|
}
|
|
memcpy(pResponseInfo, table->table, sizeof(MIB_TCPROW) *
|
|
table->dwNumEntries);
|
|
|
|
/* calculate the length of the data in the output buffer */
|
|
*pcbResponseInfoLen = sizeof(MIB_TCPROW) *
|
|
table->dwNumEntries;
|
|
|
|
HeapFree( GetProcessHeap(), 0, table );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
FIXME ("Command ID Not Supported -> toi_id=0x%lx, toi_entity={tei_entity=0x%lx, tei_instance=0x%lx}, toi_class=0x%lx\n",
|
|
pcommand->toi_id, pcommand->toi_entity.tei_entity,
|
|
pcommand->toi_entity.tei_instance, pcommand->toi_class);
|
|
|
|
return (ERROR_BAD_ENVIRONMENT);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
default:
|
|
{
|
|
FIXME ("Command ID Not Supported -> toi_id=0x%lx, toi_entity={tei_entity=0x%lx, tei_instance=0x%lx}, toi_class=0x%lx\n",
|
|
pcommand->toi_id, pcommand->toi_entity.tei_entity,
|
|
pcommand->toi_entity.tei_instance, pcommand->toi_class);
|
|
|
|
return (ERROR_BAD_ENVIRONMENT);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case WSCNTL_TCPIP_ICMP_ECHO:
|
|
{
|
|
unsigned int addr = *(unsigned int*)pRequestInfo;
|
|
#if 0
|
|
int timeout= *(unsigned int*)(inbuf+4);
|
|
short x1 = *(unsigned short*)(inbuf+8);
|
|
short sendbufsize = *(unsigned short*)(inbuf+10);
|
|
char x2 = *(unsigned char*)(inbuf+12);
|
|
char ttl = *(unsigned char*)(inbuf+13);
|
|
char service = *(unsigned char*)(inbuf+14);
|
|
char type= *(unsigned char*)(inbuf+15); /* 0x2: don't fragment*/
|
|
#endif
|
|
|
|
FIXME("(ICMP_ECHO) to 0x%08x stub\n", addr);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
FIXME("Protocol Not Supported -> protocol=0x%x, action=0x%x, Request=%p, RequestLen=%p, Response=%p, ResponseLen=%p\n",
|
|
protocol, action, pRequestInfo, pcbRequestInfoLen, pResponseInfo, pcbResponseInfoLen);
|
|
|
|
return (WSAEOPNOTSUPP);
|
|
|
|
}
|
|
|
|
return (WSCTL_SUCCESS);
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
* WSARecvEx (WSOCK32.1107)
|
|
*
|
|
* WSARecvEx is a Microsoft specific extension to winsock that is identical to recv
|
|
* except that has an in/out argument call flags that has the value MSG_PARTIAL ored
|
|
* into the flags parameter when a partial packet is read. This only applies to
|
|
* sockets using the datagram protocol. This method does not seem to be implemented
|
|
* correctly by microsoft as the winsock implementation does not set the MSG_PARTIAL
|
|
* flag when a fragmented packet arrives.
|
|
*/
|
|
INT WINAPI WSARecvEx(SOCKET s, char *buf, INT len, INT *flags)
|
|
{
|
|
FIXME("(WSARecvEx) partial packet return value not set\n");
|
|
return recv(s, buf, len, *flags);
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* s_perror (WSOCK32.1108)
|
|
*/
|
|
void WINAPI s_perror(LPCSTR message)
|
|
{
|
|
FIXME("(%s): stub\n",message);
|
|
return;
|
|
}
|