// 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

