From c9d258274d2cc9e65327e4626bd87400093d71c4 Mon Sep 17 00:00:00 2001 From: James Hatheway Date: Sun, 23 Jul 2000 19:28:24 +0000 Subject: [PATCH] Adds an initial WSAIoctl function with support for only the SIO_GET_INTERFACE_LIST command. Adds initial code to WsControl function to support the WSCNTL_TCPIP_QUERY_INFO command. --- dlls/winsock/socket.c | 285 +++++++++++++++++++ dlls/winsock/ws2_32.spec | 2 +- dlls/wsock32/socket.c | 717 +++++++++++++++++++++++++++++++++++++++++++---- dlls/wsock32/wscontrol.h | 151 ++++++++++ 4 files changed, 1097 insertions(+), 58 deletions(-) rewrite dlls/wsock32/socket.c (68%) create mode 100644 dlls/wsock32/wscontrol.h diff --git a/dlls/winsock/socket.c b/dlls/winsock/socket.c index 254f42eff99..4b4e3915b28 100644 --- a/dlls/winsock/socket.c +++ b/dlls/winsock/socket.c @@ -69,6 +69,9 @@ #ifdef HAVE_RESOLV_H # include #endif +#ifdef HAVE_NET_IF_H +# include +#endif #include "wine/winbase16.h" #include "wingdi.h" @@ -84,6 +87,7 @@ #include "server.h" #include "debugtools.h" + DEFAULT_DEBUG_CHANNEL(winsock) #define DEBUG_SOCKADDR 0 @@ -119,6 +123,10 @@ typedef struct /* WSAAsyncSelect() control struct */ #define WSI_BLOCKINGCALL 0x00000001 /* per-thread info flags */ #define WSI_BLOCKINGHOOK 0x00000002 /* 32-bit callback */ +#define PROCFS_NETDEV_FILE "/proc/net/dev" /* Points to the file in the /proc fs + that lists the network devices. + Do we need an #ifdef LINUX for this? */ + typedef struct _WSINFO { DWORD dwThisProcess; @@ -148,6 +156,9 @@ int WS_dup_he(LPWSINFO pwsi, struct hostent* p_he, int flag); int WS_dup_pe(LPWSINFO pwsi, struct protoent* p_pe, int flag); int WS_dup_se(LPWSINFO pwsi, struct servent* p_se, int flag); +int WSAIOCTL_GetInterfaceCount(void); +int WSAIOCTL_GetInterfaceName(int intNumber, char *intName); + UINT16 wsaErrno(void); UINT16 wsaHerrno(void); @@ -1270,6 +1281,280 @@ SEGPTR WINAPI WINSOCK_inet_ntoa16(struct in_addr in) return retVal ? SEGPTR_GET(retVal) : (SEGPTR)NULL; } + +/********************************************************************** + * WSAIoctl (WS2_32) + * + * + * FIXME: Only SIO_GET_INTERFACE_LIST option implemented. + */ +INT WINAPI WSAIoctl (SOCKET s, + DWORD dwIoControlCode, + LPVOID lpvInBuffer, + DWORD cbInBuffer, + LPVOID lpbOutBuffer, + DWORD cbOutBuffer, + LPDWORD lpcbBytesReturned, + LPWSAOVERLAPPED lpOverlapped, + LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) +{ + LPWSINFO pwsi = WINSOCK_GetIData(); + + if( _check_ws(pwsi, s) ) + { + int fd = _get_sock_fd(s); + + switch( dwIoControlCode ) + { + case SIO_GET_INTERFACE_LIST: + { + INTERFACE_INFO* intArray = (INTERFACE_INFO*)lpbOutBuffer; + int i, numInt; + struct ifreq ifInfo; + char ifName[512]; + + + TRACE ("-> SIO_GET_INTERFACE_LIST request\n"); + + numInt = WSAIOCTL_GetInterfaceCount(); + if (numInt < 0) + { + ERR ("Unable to open /proc filesystem to determine number of network interfaces!\n"); + close(fd); + WSASetLastError(WSAEINVAL); + return (SOCKET_ERROR); + } + + for (i=0; iiiAddress.AddressIn.sin_family = AF_INET; + intArray->iiAddress.AddressIn.sin_port = ipTemp->sin_port; + intArray->iiAddress.AddressIn.sin_addr.ws_addr = ipTemp->sin_addr.S_un.S_addr; + } + + /* Broadcast Address */ + strcpy (ifInfo.ifr_name, ifName); + if (ioctl(fd, SIOCGIFBRDADDR, &ifInfo) < 0) + { + ERR ("Error obtaining Broadcast IP address\n"); + close(fd); + WSASetLastError(WSAEINVAL); + return (SOCKET_ERROR); + } + else + { + struct ws_sockaddr_in *ipTemp = (struct ws_sockaddr_in *)&ifInfo.ifr_broadaddr; + + intArray->iiBroadcastAddress.AddressIn.sin_family = AF_INET; + intArray->iiBroadcastAddress.AddressIn.sin_port = ipTemp->sin_port; + intArray->iiBroadcastAddress.AddressIn.sin_addr.ws_addr = ipTemp->sin_addr.S_un.S_addr; + } + + /* Subnet Mask */ + strcpy (ifInfo.ifr_name, ifName); + if (ioctl(fd, SIOCGIFNETMASK, &ifInfo) < 0) + { + ERR ("Error obtaining Subnet IP address\n"); + close(fd); + WSASetLastError(WSAEINVAL); + return (SOCKET_ERROR); + } + else + { + struct ws_sockaddr_in *ipTemp = (struct ws_sockaddr_in *)&ifInfo.ifr_netmask; + + intArray->iiNetmask.AddressIn.sin_family = AF_INET; + intArray->iiNetmask.AddressIn.sin_port = ipTemp->sin_port; + intArray->iiNetmask.AddressIn.sin_addr.ws_addr = ipTemp->sin_addr.S_un.S_addr; + } + + /* Socket Status Flags */ + strcpy(ifInfo.ifr_name, ifName); + if (ioctl(fd, SIOCGIFFLAGS, &ifInfo) < 0) + { + ERR ("Error obtaining status flags for socket!\n"); + close(fd); + WSASetLastError(WSAEINVAL); + return (SOCKET_ERROR); + } + else + { + /* FIXME - Is this the right flag to use? */ + intArray->iiFlags = ifInfo.ifr_flags; + } + intArray++; /* Prepare for another interface */ + } + + /* Calculate the size of the array being returned */ + *lpcbBytesReturned = sizeof(INTERFACE_INFO) * numInt; + break; + } + + default: + { + WARN("\tunsupported WS_IOCTL cmd (%08lx)\n", dwIoControlCode); + close(fd); + WSASetLastError(WSAEOPNOTSUPP); + return (SOCKET_ERROR); + } + } + + /* Function executed with no errors */ + close(fd); + return (0); + } + else + { + WSASetLastError(WSAENOTSOCK); + return (SOCKET_ERROR); + } +} + + +/* + Helper function for WSAIoctl - Get count of the number of interfaces + by parsing /proc filesystem. +*/ +int WSAIOCTL_GetInterfaceCount(void) +{ + FILE *procfs; + char buf[512]; /* Size doesn't matter, something big */ + int intcnt=0; + + + /* Open /proc filesystem file for network devices */ + procfs = fopen(PROCFS_NETDEV_FILE, "r"); + if (!procfs) + { + /* If we can't open the file, return an error */ + return (-1); + } + + /* Omit first two lines, they are only headers */ + fgets(buf, sizeof buf, procfs); + fgets(buf, sizeof buf, procfs); + + while (fgets(buf, sizeof buf, procfs)) + { + /* Each line in the file represents a network interface */ + intcnt++; + } + + fclose(procfs); + return(intcnt); +} + + +/* + Helper function for WSAIoctl - Get name of device from interface number + by parsing /proc filesystem. +*/ +int WSAIOCTL_GetInterfaceName(int intNumber, char *intName) +{ + FILE *procfs; + char buf[512]; /* Size doesn't matter, something big */ + int i; + + /* Open /proc filesystem file for network devices */ + procfs = fopen(PROCFS_NETDEV_FILE, "r"); + if (!procfs) + { + /* If we can't open the file, return an error */ + return (-1); + } + + /* Omit first two lines, they are only headers */ + fgets(buf, sizeof(buf), procfs); + fgets(buf, sizeof(buf), procfs); + + for (i=0; i +#include "windef.h" +#include "debugtools.h" +#include "winsock2.h" +#include "winnt.h" +#include "wscontrol.h" +#include +#include +#include +#include +#include +#ifdef HAVE_NET_IF_H +# include +#endif + +DEFAULT_DEBUG_CHANNEL(winsock); + + +/*********************************************************************** + * WsControl() + * + * 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" + * Date: 1997/08/17 + * + * WSCNTL_TCPIP_QUERY_INFO option is partially implemeted 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. + * STATUS_BUFFER_TOO_SMALL is returned if the output buffer length + * (*pcbResponseInfoLen) is too small, otherwise errors return -1. + * + * It doesn't seem to generate errors that can be retrieved by + * WSAGetLastError(). + * + */ + +DWORD WINAPI WsControl(DWORD protocoll, + 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; + + 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: + { + switch (pcommand->toi_id) + { + /* + ENTITY_LIST_ID seems to get number of adapters in the system. + (almost like an index to be used when calling other WsControl options) + */ + case ENTITY_LIST_ID: + { + TDIEntityID *baseptr = pResponseInfo; + int numInt = 0, i; + + if (pcommand->toi_class != INFO_CLASS_GENERIC && + pcommand->toi_type != INFO_TYPE_PROVIDER) + { + FIXME ("Unexpected Option for ENTITY_LIST_ID request -> toi_class=0x%lx, toi_type=0x%lx\n", + pcommand->toi_class, pcommand->toi_type); + return (WSAEOPNOTSUPP); + } + + numInt = WSCNTL_GetInterfaceCount(); + if (numInt < 0) + { + ERR ("Unable to open /proc filesystem to determine number of network interfaces!\n"); + return (-1); + } + + if (*pcbResponseInfoLen < sizeof(TDIEntityID)*(numInt*2) ) + { + return (STATUS_BUFFER_TOO_SMALL); + } + + /* 0 it out first */ + memset(baseptr, 0, sizeof(TDIEntityID)*(numInt*2)); + + for (i=0; itei_entity = CL_NL_ENTITY; baseptr->tei_instance = i; baseptr++; + baseptr->tei_entity = IF_ENTITY; baseptr->tei_instance = i; baseptr++; + } + + /* Calculate size of out buffer */ + *pcbResponseInfoLen = sizeof(TDIEntityID)*(numInt*2); + + break; + } + + + /* ENTITY_TYPE_ID is used to obtain simple information about a + network card, such as MAC Address, description, interface type, + number of network addresses, etc. */ + case ENTITY_TYPE_ID: /* ALSO: IP_MIB_STATS_ID */ + { + if (pcommand->toi_class == INFO_CLASS_GENERIC && pcommand->toi_type == INFO_TYPE_PROVIDER) + { + if (pcommand->toi_entity.tei_entity == IF_ENTITY) + { + * ((ULONG *)pResponseInfo) = IF_MIB; + + /* Calculate size of out buffer */ + *pcbResponseInfoLen = sizeof (ULONG); + + } + else if (pcommand->toi_entity.tei_entity == CL_NL_ENTITY) + { + * ((ULONG *)pResponseInfo) = CL_NL_IP; + + /* Calculate size of out buffer */ + *pcbResponseInfoLen = sizeof (ULONG); + } + } + else if (pcommand->toi_class == INFO_CLASS_PROTOCOL && + pcommand->toi_type == INFO_TYPE_PROVIDER) + { + if (pcommand->toi_entity.tei_entity == IF_ENTITY) + { + /* In this case, we are requesting specific information about a + a particular network adapter. (MAC Address, speed, data transmitted/received, + etc.) + */ + IFEntry *IntInfo = (IFEntry *) pResponseInfo; + char ifName[512]; + struct ifreq ifInfo; + int sock; + + + if (!WSCNTL_GetInterfaceName(pcommand->toi_entity.tei_instance, ifName)) + { + ERR ("Unable to parse /proc filesystem!\n"); + return (-1); + } + + /* Get a socket so that we can use ioctl */ + if ( (sock = socket (AF_INET, SOCK_DGRAM, 0)) < 0) + { + ERR ("Error creating socket!\n"); + return (-1); + } + + /* 0 out return structure first */ + memset (IntInfo, 0, sizeof(IFEntry)); + + /* Interface ID */ + IntInfo->if_index = pcommand->toi_entity.tei_instance; + + /* MAC Address */ + strcpy(ifInfo.ifr_name, ifName); + if (ioctl(sock, SIOCGIFHWADDR, &ifInfo) < 0) + { + ERR ("Error obtaining MAC Address!\n"); + close(sock); + return (-1); + } + else + { + /* FIXME: Is it correct to assume size of 6? */ + memcpy(IntInfo->if_physaddr, ifInfo.ifr_hwaddr.sa_data, 6); + IntInfo->if_physaddrlen=6; + } + + /* Interface name and length */ + strcpy (IntInfo->if_descr, ifName); + IntInfo->if_descrlen= strlen (IntInfo->if_descr); + + /* Obtain bytes transmitted/received for interface */ + if ( (WSCNTL_GetTransRecvStat(pcommand->toi_entity.tei_instance, + &IntInfo->if_inoctets, &IntInfo->if_outoctets)) < 0) + { + ERR ("Error obtaining transmit/receive stats for the network interface!\n"); + close(sock); + return (-1); + } + + + /* FIXME: How should the below be properly calculated? ******************/ + IntInfo->if_type = 0x6; /* Ethernet (?) */ + IntInfo->if_speed = 1000000; /* Speed of interface (bits per second?) */ + /************************************************************************/ + + close(sock); + *pcbResponseInfoLen = sizeof (IFEntry) + IntInfo->if_descrlen; + } + else if (pcommand->toi_entity.tei_entity == CL_NL_ENTITY) + { + IPSNMPInfo *infoStruc = (IPSNMPInfo *) pResponseInfo; + int numInt; + + /* This case is used to obtain general statistics about the + network */ + + if (*pcbResponseInfoLen < sizeof(IPSNMPInfo) ) + { + return (STATUS_BUFFER_TOO_SMALL); + } + else + { + /* 0 it out first */ + memset(infoStruc, 0, sizeof(IPSNMPInfo)); + + /* Get the number of interfaces */ + numInt = WSCNTL_GetInterfaceCount(); + if (numInt < 0) + { + ERR ("Unable to open /proc filesystem to determine number of network interfaces!\n"); + return (-1); + } + + infoStruc->ipsi_numif = numInt; /* # of interfaces */ + infoStruc->ipsi_numaddr = numInt; /* # of addresses */ + infoStruc->ipsi_numroutes = numInt; /* # of routes ~ FIXME - Is this right? */ + + /* FIXME: How should the below be properly calculated? ******************/ + infoStruc->ipsi_forwarding = 0x0; + infoStruc->ipsi_defaultttl = 0x0; + infoStruc->ipsi_inreceives = 0x0; + infoStruc->ipsi_inhdrerrors = 0x0; + infoStruc->ipsi_inaddrerrors = 0x0; + infoStruc->ipsi_forwdatagrams = 0x0; + infoStruc->ipsi_inunknownprotos = 0x0; + infoStruc->ipsi_indiscards = 0x0; + infoStruc->ipsi_indelivers = 0x0; + infoStruc->ipsi_outrequests = 0x0; + infoStruc->ipsi_routingdiscards = 0x0; + infoStruc->ipsi_outdiscards = 0x0; + infoStruc->ipsi_outnoroutes = 0x0; + infoStruc->ipsi_reasmtimeout = 0x0; + infoStruc->ipsi_reasmreqds = 0x0; + infoStruc->ipsi_reasmoks = 0x0; + infoStruc->ipsi_reasmfails = 0x0; + infoStruc->ipsi_fragoks = 0x0; + infoStruc->ipsi_fragfails = 0x0; + infoStruc->ipsi_fragcreates = 0x0; + /************************************************************************/ + + /* Calculate size of out buffer */ + *pcbResponseInfoLen = sizeof(IPSNMPInfo); + } + } + } + else + { + FIXME ("Unexpected Option for ENTITY_TYPE_ID request -> toi_class=0x%lx, toi_type=0x%lx\n", + pcommand->toi_class, pcommand->toi_type); + + return (WSAEOPNOTSUPP); + } + + break; + } + + + /* IP_MIB_ADDRTABLE_ENTRY_ID is used to obtain more detailed information about a + particular network adapter */ + case IP_MIB_ADDRTABLE_ENTRY_ID: + { + IPAddrEntry *baseIPInfo = (IPAddrEntry *) pResponseInfo; + char ifName[512]; + struct ifreq ifInfo; + int sock; + + if (*pcbResponseInfoLen < sizeof(IPAddrEntry)) + { + return (STATUS_BUFFER_TOO_SMALL); + } + + if (!WSCNTL_GetInterfaceName(pcommand->toi_entity.tei_instance, ifName)) + { + ERR ("Unable to parse /proc filesystem!\n"); + return (-1); + } + + + /* Get a socket so we can use ioctl */ + if ( (sock = socket (AF_INET, SOCK_DGRAM, 0)) < 0) + { + ERR ("Error creating socket!\n"); + return (-1); + } + + /* 0 it out first */ + memset(baseIPInfo, 0, sizeof(IPAddrEntry) ); + + /* Interface Id */ + baseIPInfo->iae_index = pcommand->toi_entity.tei_instance; + + /* IP Address */ + strcpy (ifInfo.ifr_name, ifName); + ifInfo.ifr_addr.sa_family = AF_INET; + if (ioctl(sock, SIOCGIFADDR, &ifInfo) < 0) + { + baseIPInfo->iae_addr = 0x0; + } + else + { + struct ws_sockaddr_in *ipTemp = (struct ws_sockaddr_in *)&ifInfo.ifr_addr; + baseIPInfo->iae_addr = ipTemp->sin_addr.S_un.S_addr; + } + + /* Broadcast Address */ + strcpy (ifInfo.ifr_name, ifName); + if (ioctl(sock, SIOCGIFBRDADDR, &ifInfo) < 0) + { + baseIPInfo->iae_bcastaddr = 0x0; + } + else + { + struct ws_sockaddr_in *ipTemp = (struct ws_sockaddr_in *)&ifInfo.ifr_broadaddr; + baseIPInfo->iae_bcastaddr = ipTemp->sin_addr.S_un.S_addr; + } + + /* Subnet Mask */ + strcpy(ifInfo.ifr_name, ifName); + if (ioctl(sock, SIOCGIFNETMASK, &ifInfo) < 0) + { + baseIPInfo->iae_mask = 0x0; + } + else + { + struct ws_sockaddr_in *ipTemp = (struct ws_sockaddr_in *)&ifInfo.ifr_netmask; + baseIPInfo->iae_mask = ipTemp->sin_addr.S_un.S_addr; + } + + /* FIXME: How should the below be properly calculated? ******************/ + baseIPInfo->iae_reasmsize = 0x0; + baseIPInfo->iae_context = 0x0; + baseIPInfo->iae_pad = 0x0; + /************************************************************************/ + + /* Calculate size of out buffer */ + *pcbResponseInfoLen = sizeof(IPAddrEntry); + close(sock); + 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, toi_type=0x%lx\n", + pcommand->toi_id, pcommand->toi_entity.tei_entity, pcommand->toi_entity.tei_instance, + pcommand->toi_class, pcommand->toi_type); + + return (WSAEOPNOTSUPP); + } + } + + 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("Protocoll Not Supported -> protocoll=0x%lx, action=0x%lx, Request=%p, RequestLen=%p, Response=%p, ResponseLen=%p\n", + protocoll, action, pRequestInfo, pcbRequestInfoLen, pResponseInfo, pcbResponseInfoLen); + + return (WSAEOPNOTSUPP); + } + } + + + return (WSCTL_SUCCESS); +} + + + +/* + Helper function for WsControl - Get count of the number of interfaces + by parsing /proc filesystem. +*/ +int WSCNTL_GetInterfaceCount(void) +{ + FILE *procfs; + char buf[512]; /* Size doesn't matter, something big */ + int intcnt=0; + + + /* Open /proc filesystem file for network devices */ + procfs = fopen(PROCFS_NETDEV_FILE, "r"); + if (!procfs) + { + /* If we can't open the file, return an error */ + return (-1); + } + + /* Omit first two lines, they are only headers */ + fgets(buf, sizeof buf, procfs); + fgets(buf, sizeof buf, procfs); + + while (fgets(buf, sizeof buf, procfs)) + { + /* Each line in the file represents a network interface */ + intcnt++; + } + + fclose(procfs); + return(intcnt); +} + + +/* + Helper function for WsControl - Get name of device from interface number + by parsing /proc filesystem. +*/ +int WSCNTL_GetInterfaceName(int intNumber, char *intName) +{ + FILE *procfs; + char buf[512]; /* Size doesn't matter, something big */ + int i; + + /* Open /proc filesystem file for network devices */ + procfs = fopen(PROCFS_NETDEV_FILE, "r"); + if (!procfs) + { + /* If we can't open the file, return an error */ + return (-1); + } + + /* Omit first two lines, they are only headers */ + fgets(buf, sizeof(buf), procfs); + fgets(buf, sizeof(buf), procfs); + + for (i=0; i