| /* |
| * 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 = (TDIObjectID *)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 = (TDIEntityID *)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(), HEAP_ZERO_MEMORY, 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 %lu\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 = (PULONG)pResponseInfo; |
| |
| if (!pcbResponseInfoLen) |
| return ERROR_BAD_ENVIRONMENT; |
| if (*pcbResponseInfoLen < sizeof(ULONG) * 2) |
| return (ERROR_LOCK_VIOLATION); |
| GetIpNetTable(NULL, &size, FALSE); |
| table = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 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((PMIB_IPSTATS)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((PMIB_UDPSTATS)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((PMIB_TCPSTATS)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 = (PMIB_IPADDRROW) 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(), HEAP_ZERO_MEMORY, 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%lx:\n", index); |
| TRACE("IP 0x%08lx, mask 0x%08lx\n", table->table[i].dwAddr, |
| table->table[i].dwMask); |
| memcpy(baseIPInfo, &table->table[i], sizeof(MIB_IPADDRROW)); |
| 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 = (IPRouteEntry *) pResponseInfo; |
| |
| if (!pcbResponseInfoLen) |
| return ERROR_BAD_ENVIRONMENT; |
| GetIpForwardTable(NULL, &routeTableSize, FALSE); |
| numRoutes = min(routeTableSize - sizeof(MIB_IPFORWARDTABLE), |
| 0) / sizeof(MIB_IPFORWARDROW) + 1; |
| if (*pcbResponseInfoLen < sizeof(IPRouteEntry) * numRoutes) |
| return (ERROR_LOCK_VIOLATION); |
| table = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 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; |
| GetIpNetTable(NULL, &arpTableSize, FALSE); |
| numEntries = min(arpTableSize - sizeof(MIB_IPNETTABLE), |
| 0) / sizeof(MIB_IPNETROW) + 1; |
| if (*pcbResponseInfoLen < sizeof(MIB_IPNETROW) * numEntries) |
| return (ERROR_LOCK_VIOLATION); |
| table = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 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; |
| GetTcpTable(NULL, &tcpTableSize, FALSE); |
| numEntries = min(tcpTableSize - sizeof(MIB_TCPTABLE), |
| 0) / sizeof(MIB_TCPROW) + 1; |
| if (*pcbResponseInfoLen < sizeof(MIB_TCPROW) * numEntries) |
| return (ERROR_LOCK_VIOLATION); |
| table = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 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%lx, action=0x%lx, 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; |
| } |