Winsock Programmer’s FAQ Articles: WsControl() Revealed |
by Tom Sanfilippo (tsanfilippo at earthlink dot net)
WsControl()
is used to get interface info in the Windows
95 program winipcfg.exe. Apparently it can also be used to set IP
config information as well. I have not tested setting, but with the
info below, you should be able to do a lot of query-only calls. In
theory you can also trace the "set" calls made by winipcfg.exe and
deduce some of the input parameters for those calls. This is left as
an exercise for the reader.
In the mode used in winipcfg.exe, WsControl()
basically
gives you access to SNMP query info, such as route table entries,
interface list entries, adapter description entries, etc. (Pretty
exciting!!) However, like others, I needed it for a Windows 95
application that cannot require Winsock 2. If you can require
Winsock 2, you are probably better off using WSIoctl()
or
GetInterfaceInfo()
to get the info provided by WsControl()
(at least as much as is exposed by its use in winipcfg.exe).
I used a hook DLL to hook and dump all the parameters, then I looked for patterns in the input and output. Then I wrote a test program to test out the theoretical signatures. I showed this to Thomas Divine (tdivine at pcausa dot com), and he recognized that the parameters I came up with were similar to parameters in a WSHelper sample in the NT4 DDK. I then took that sample and was able to deduce even more parameters. See the section entitled "Parameter Deduction Notes" for more info.
Well, the official line from Microsoft is that if you need this information programmatically from a Windows 95 application without Winsock 2, then you should have your application run winipcfg.exe in batch mode (yes there is a batch mode), and pipe the output to a file, then read the file into your application. Being that this would be almost as much fun as embedding a Tcl interpreter, I though it would be more interesting to see how they did it.
You need a few header files to make this code compile:
\ddk\src\network\inc\tdiinfo.h \ddk\src\network\wshsmple\smpletcp.h
[Ed. The author referenced the Windows NT 4.0 DDK for these headers, but that is no longer available. You’ll have to look on MSDN to see if you can find a current DDK that still has these files.]
WsControl()
CallThe WsControl()
function looks something like this:
int WsControl( DWORD protocol, DWORD action, LPVOID pRequestInfo, LPDWORD pcbRequestInfoLen, LPVOID pResponseInfo, LPDWORD pcbResponseInfoLen );
WsControl()
returns 0 on success. Otherwise a non-zero value
is returned. It might return STATUS_BUFFER_TOO_SMALL
if the output buffer length (*pcbResponseInfoLen
) is too
small. It doesn’t seem to generate errors that can be retrieved
by WSAGetLastError()
.
pTcpRequestInfo
is of type
PTCP_REQUEST_QUERY_INFORMATION_EX
, or of type
PTCP_REQUEST_SET_INFORMATION_EX
. For the calls I traced in
winipcfg.exe, the inputs common to all query calls were:
protocol = IPPROTO_TCP; action = WSCTL_TCP_QUERY_INFORMATION;
Not all output structures could be found for the queries made
in winipcfg.exe. As a result, I had to make some up. The made up
ones are defined in the sample code below. Note: you may be able to
deduce more of the parameter definitions by looking at the SNMP,
WSAIoctl()
, or IP Helper data structures in the current Platform
SDK.
Again, many thanks are due to Thomas Divine for pointing out the updated wshsmple in the NT4 DDK. The headers in that sample allowed the input parameters to finally be discovered.
The sample code below works on both Windows 95 and Windows 98. The sample emulates all the queries done by winipcfg.exe as it starts up.
// wsctl.cpp // Demonstrates possible calling params for the undocumented wsock32.dll // api "WsControl", which is used by winipcfg.exe on win95/98. wsock32.dll // is the 32-bit thunk to old winsock implementations (I think). // Gets ip info for the current interface list. // Tom Sanfilippo (tsanfilippo at earthlink dot net) // December 12, 1999 // Thanks are due to Thomas F. Divine (tdivine at pcausa dot com) // for pointing out the updated wshsmple in the NT4DDK. // The headers in that sample allowed the input parameters // to finally be discovered. #include <windows.h> #include <stdio.h> #include <winsock.h> #include <crtdbg.h> #include "tdiinfo.h" // from recent NT4DDK "\ddk\src\network\inc\tdiinfo.h" #include "smpletcp.h" // from recent NT4DDK "\ddk\src\network\wshsmple\smpletcp.h" typedef int (__stdcall * WsControlProc) (DWORD, DWORD, LPVOID, LPDWORD, LPVOID, LPDWORD); typedef int (__stdcall * WSAGetLastErrorProc) (void); typedef int (__stdcall * WSAStartupProc) (WORD wVersionRequested, LPWSADATA lpWSAData); typedef int (__stdcall * WSACleanupProc) (void); WsControlProc WsControl = NULL; WSAGetLastErrorProc WSAGetLastError1 = NULL; WSAStartupProc WSAStartup1 = NULL; WSACleanupProc WSACleanup1 = NULL; #define WSCTL_TCP_QUERY_INFORMATION 0 #define WSCTL_TCP_SET_INFORMATION 1 //?? #define IP_MIB_ROUTETABLE_ENTRY_ID 0x101 #pragma pack(push,1) typedef struct IPRouteEntry { ulong ire_addr; ulong ire_index; //matches if_index in IFEntry and iae_index in IPAddrEntry ulong ire_metric; ulong ire_unk1; //?? ulong ire_unk2; //?? ulong ire_unk3; //?? ulong ire_gw; ulong ire_unk4; //?? ulong ire_unk5; //?? ulong ire_unk6; //?? ulong ire_mask; ulong ire_unk7; //?? } IPRouteEntry; #pragma pack(pop) int main(int argc, char **argv) { int result = 0; HMODULE hModule; WSADATA WSAData; hModule = LoadLibrary("wsock32.dll"); if (!hModule) { fprintf(stderr, "LoadLibrary failed for wsock32.dll (%ld)\n", GetLastError()); return EXIT_FAILURE; } WsControl = (WsControlProc) GetProcAddress(hModule, "WsControl"); if (!WsControl) { fprintf(stderr, "GetProcAddress failed for WsControl (%ld)\n", GetLastError()); FreeLibrary(hModule); return EXIT_FAILURE; } WSAGetLastError1 = (WSAGetLastErrorProc) GetProcAddress(hModule, "WSAGetLastError"); if (!WSAGetLastError1) { fprintf(stderr, "GetProcAddress failed for WSAGetLastError (%ld)\n", GetLastError()); FreeLibrary(hModule); return EXIT_FAILURE; } WSAStartup1 = (WSAStartupProc) GetProcAddress(hModule, "WSAStartup"); if (!WSAStartup1) { fprintf(stderr, "GetProcAddress failed for WSAStartup (%ld)\n", GetLastError()); FreeLibrary(hModule); return EXIT_FAILURE; } WSACleanup1 = (WSACleanupProc) GetProcAddress(hModule, "WSACleanup"); if (!WSACleanup1) { fprintf(stderr, "GetProcAddress failed for WSACleanup (%ld)\n", GetLastError()); FreeLibrary(hModule); return EXIT_FAILURE; } result = WSAStartup1(MAKEWORD(1, 1), &WSAData); if (result) { fprintf(stderr, "WSAStartup failed (%ld)\n", result); FreeLibrary(hModule); return EXIT_FAILURE; } TCP_REQUEST_QUERY_INFORMATION_EX tcpRequestQueryInfoEx; memset(&tcpRequestQueryInfoEx, 0, sizeof(tcpRequestQueryInfoEx)); tcpRequestQueryInfoEx.ID.toi_entity.tei_entity = GENERIC_ENTITY; tcpRequestQueryInfoEx.ID.toi_entity.tei_instance = 0; tcpRequestQueryInfoEx.ID.toi_class = INFO_CLASS_GENERIC; tcpRequestQueryInfoEx.ID.toi_type = INFO_TYPE_PROVIDER; tcpRequestQueryInfoEx.ID.toi_id = ENTITY_LIST_ID; DWORD tcpRequestBufSize = sizeof(tcpRequestQueryInfoEx); //this probably allocates too much space; not sure if MAX_TDI_ENTITIES //represents the max number of entities that can be returned or, if it //is the highest entity value that can be defined. DWORD entityIdsBufSize = MAX_TDI_ENTITIES * sizeof(TDIEntityID); TDIEntityID *entityIds = (TDIEntityID *) calloc(entityIdsBufSize, 1); result = WsControl(IPPROTO_TCP, WSCTL_TCP_QUERY_INFORMATION, &tcpRequestQueryInfoEx, &tcpRequestBufSize, entityIds, &entityIdsBufSize); if (result) { fprintf(stderr, "%s(%d) WsControl failed (%ld)\n", __FILE__, __LINE__, WSAGetLastError1()); WSACleanup1(); FreeLibrary(hModule); return EXIT_FAILURE; } //...after the call we compute: DWORD entityCount = entityIdsBufSize / sizeof(TDIEntityID); DWORD i; DWORD ifCount = 0; //print out the interface info for the generic interfaces for (i = 0; i < entityCount; i++) { if (entityIds[i].tei_entity == IF_ENTITY) { ++ifCount; //see if the iterface supports snmp mib-2 info memset(&tcpRequestQueryInfoEx, 0, sizeof(tcpRequestQueryInfoEx)); tcpRequestQueryInfoEx.ID.toi_entity = entityIds[i]; tcpRequestQueryInfoEx.ID.toi_class = INFO_CLASS_GENERIC; tcpRequestQueryInfoEx.ID.toi_type = INFO_TYPE_PROVIDER; tcpRequestQueryInfoEx.ID.toi_id = ENTITY_TYPE_ID; ULONG entityType; DWORD entityTypeSize = sizeof(entityType); result = WsControl(IPPROTO_TCP, WSCTL_TCP_QUERY_INFORMATION, &tcpRequestQueryInfoEx, &tcpRequestBufSize, &entityType, &entityTypeSize); if (result) { fprintf(stderr, "%s(%d) WsControl failed (%ld)\n", __FILE__, __LINE__, WSAGetLastError1()); WSACleanup1(); FreeLibrary(hModule); return EXIT_FAILURE; } if (entityType == IF_MIB) { // Supports MIB-2 interface. //get snmp mib-2 info tcpRequestQueryInfoEx.ID.toi_class = INFO_CLASS_PROTOCOL; tcpRequestQueryInfoEx.ID.toi_id = IF_MIB_STATS_ID; //note: win95 winipcfg use 130 for MAX_IFDESCR_LEN while //ddk\src\network\wshsmple\SMPLETCP.H defines it as 256 //we are trying to dup the winipcfg parameters for now DWORD ifEntrySize = sizeof(IFEntry) + 128 + 1; IFEntry *ifEntry = (IFEntry *) calloc(ifEntrySize, 1); result = WsControl(IPPROTO_TCP, WSCTL_TCP_QUERY_INFORMATION, &tcpRequestQueryInfoEx, &tcpRequestBufSize, ifEntry, &ifEntrySize); if (result) { fprintf(stderr, "%s(%d) WsControl failed (%ld)\n", __FILE__, __LINE__, WSAGetLastError1()); WSACleanup1(); FreeLibrary(hModule); return EXIT_FAILURE; } //print interface index and description *(ifEntry->if_descr + ifEntry->if_descrlen) = 0; fprintf(stdout, "IF Index %lu %s\n", ifEntry->if_index, ifEntry->if_descr); } } } //find the ip interface for (i = 0; i < entityCount; i++) { if (entityIds[i].tei_entity == CL_NL_ENTITY) { //get ip interface info memset(&tcpRequestQueryInfoEx, 0, sizeof(tcpRequestQueryInfoEx)); tcpRequestQueryInfoEx.ID.toi_entity = entityIds[i]; tcpRequestQueryInfoEx.ID.toi_class = INFO_CLASS_GENERIC; tcpRequestQueryInfoEx.ID.toi_type = INFO_TYPE_PROVIDER; tcpRequestQueryInfoEx.ID.toi_id = ENTITY_TYPE_ID; ULONG entityType; DWORD entityTypeSize = sizeof(entityType); result = WsControl(IPPROTO_TCP, WSCTL_TCP_QUERY_INFORMATION, &tcpRequestQueryInfoEx, &tcpRequestBufSize, &entityType, &entityTypeSize); if (result) { fprintf(stderr, "%s(%d) WsControl failed (%ld)\n", __FILE__, __LINE__, WSAGetLastError1()); WSACleanup1(); FreeLibrary(hModule); return EXIT_FAILURE; } if (entityType == CL_NL_IP) { // Entity implements IP. //get ip snmp info tcpRequestQueryInfoEx.ID.toi_class = INFO_CLASS_PROTOCOL; tcpRequestQueryInfoEx.ID.toi_id = IP_MIB_STATS_ID; IPSNMPInfo ipSnmpInfo; DWORD ipSnmpInfoSize = sizeof(ipSnmpInfo); result = WsControl(IPPROTO_TCP, WSCTL_TCP_QUERY_INFORMATION, &tcpRequestQueryInfoEx, &tcpRequestBufSize, &ipSnmpInfo, &ipSnmpInfoSize); if (result) { fprintf(stderr, "%s(%d) WsControl failed (%ld)\n", __FILE__, __LINE__, WSAGetLastError1()); WSACleanup1(); FreeLibrary(hModule); return EXIT_FAILURE; } //print ip snmp info fprintf(stdout, "IP NumIfs: %lu\n", ipSnmpInfo.ipsi_numif); fprintf(stdout, "IP NumAddrs: %lu\n", ipSnmpInfo.ipsi_numaddr); fprintf(stdout, "IP NumRoutes: %lu\n", ipSnmpInfo.ipsi_numroutes); //get ip address list tcpRequestQueryInfoEx.ID.toi_id = IP_MIB_ADDRTABLE_ENTRY_ID; DWORD ipAddrEntryBufSize = sizeof(IPAddrEntry) * ifCount; IPAddrEntry *ipAddrEntry = (IPAddrEntry *) calloc(ipAddrEntryBufSize, 1); result = WsControl(IPPROTO_TCP, WSCTL_TCP_QUERY_INFORMATION, &tcpRequestQueryInfoEx, &tcpRequestBufSize, ipAddrEntry, &ipAddrEntryBufSize); if (result) { fprintf(stderr, "%s(%d) WsControl failed (%ld)\n", __FILE__, __LINE__, WSAGetLastError1()); WSACleanup1(); FreeLibrary(hModule); return EXIT_FAILURE; } //print ip address list DWORD j; for (j = 0; j < ifCount; j++) { unsigned char *addr = (unsigned char *) &ipAddrEntry[j].iae_addr; unsigned char *mask = (unsigned char *) &ipAddrEntry[j].iae_mask; fprintf(stdout, "IF Index %ld " "Address %ld.%ld.%ld.%ld " "Mask %ld.%ld.%ld.%ld\n", ipAddrEntry[j].iae_index, addr[0], addr[1], addr[2], addr[3], mask[0], mask[1], mask[2], mask[3]); } //get route table tcpRequestQueryInfoEx.ID.toi_id = IP_MIB_ROUTETABLE_ENTRY_ID; DWORD ipRouteEntryBufSize = sizeof(IPRouteEntry) * ipSnmpInfo.ipsi_numroutes; IPRouteEntry *ipRouteEntry = (IPRouteEntry *) calloc(ipRouteEntryBufSize, 1); result = WsControl(IPPROTO_TCP, WSCTL_TCP_QUERY_INFORMATION, &tcpRequestQueryInfoEx, &tcpRequestBufSize, ipRouteEntry, &ipRouteEntryBufSize); if (result) { fprintf(stderr, "%s(%d) WsControl failed (%ld)\n", __FILE__, __LINE__, WSAGetLastError1()); WSACleanup1(); FreeLibrary(hModule); return EXIT_FAILURE; } //print route table for (j = 0; j < ipSnmpInfo.ipsi_numroutes; j++) { unsigned char *addr = (unsigned char *) &ipRouteEntry[j].ire_addr; unsigned char *gw = (unsigned char *) &ipRouteEntry[j].ire_gw; unsigned char *mask = (unsigned char *) &ipRouteEntry[j].ire_mask; fprintf(stdout, "Route %ld.%ld.%ld.%ld " "IF %ld " "GW %ld.%ld.%ld.%ld " "Mask %ld.%ld.%ld.%ld " "Metric %ld\n", addr[0], addr[1], addr[2], addr[3], ipRouteEntry[j].ire_index, gw[0], gw[1], gw[2], gw[3], mask[0], mask[1], mask[2], mask[3], ipRouteEntry[j].ire_metric); } } } } WSACleanup1(); FreeLibrary(hModule); return EXIT_SUCCESS; } //end of code
[Ed. Tom included a bunch of notes from tracing calls winipcfg.exe made. For various reasons, I’ve made them available in a separate file.]
Copyright © 1999 by Tom Sanfilippo. All rights reserved.
<< BSD Sockets Compatibility |
CSocket Considered Harmful >> |
Updated Fri Dec 16 2022 12:23 MST | Go to my home page |