/* * WSOCK32 specific functions * * Copyright (C) 1993,1994,1996,1997 John Brezak, Erik Bos, Alex Korobka. */ /* FIXME: This hack is fixing a problem in WsControl. When we call socket(), it is supposed to call into ws2_32's WSOCK32_socket. The problem is that socket() is predefined in a linux system header that we are including, which is different from the WINE definition. (cdecl vs. stdapi) The result is stack corruption. The correct answer to this problem is to make winsock.h not dependent on system headers, that way all of our functions are defined consistently. Until that happens we need this hack. */ #define socket linux_socket /* */ #include "config.h" #include #include "windef.h" #include "winbase.h" #include "debugtools.h" #include "winsock2.h" #include "winnt.h" #include "wscontrol.h" #include #include #include #include #include #ifdef HAVE_SYS_SOCKIO_H # include #endif #ifdef HAVE_NET_IF_H # include #endif /* FIXME: The rest of the socket() cdecl<->stdapi stack corruption problem discussed above. */ #undef socket extern SOCKET WINAPI socket(INT af, INT type, INT protocol); /* */ 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; SOCKET 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 - Let's try to do this in a cross-platform way... */ #if defined(SIOCGIFHWADDR) /* Linux */ strcpy(ifInfo.ifr_name, ifName); if (ioctlsocket(sock, SIOCGIFHWADDR, (ULONG*)&ifInfo) < 0) { ERR ("Error obtaining MAC Address!\n"); closesocket(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; } #elif defined(SIOCGENADDR) /* Solaris */ if (ioctlsocket(sock, SIOCGENADDR, (ULONG*)&ifInfo) < 0) { ERR ("Error obtaining MAC Address!\n"); closesocket(sock); return (-1); } else { /* FIXME: Is it correct to assume size of 6? */ memcpy(IntInfo->if_physaddr, ifInfo.ifr_enaddr, 6); IntInfo->if_physaddrlen=6; } #else memset (IntInfo->if_physaddr, 0, 6); ERR ("Unable to determine MAC Address on your platform!\n"); #endif /* 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"); closesocket(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?) */ /************************************************************************/ closesocket(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; SOCKET 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 (ioctlsocket(sock, SIOCGIFADDR, (ULONG*)&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 (ioctlsocket(sock, SIOCGIFBRDADDR, (ULONG *)&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 (ioctlsocket(sock, SIOCGIFNETMASK, (ULONG *)&ifInfo) < 0) { baseIPInfo->iae_mask = 0x0; } else { /* Trying to avoid some compile problems across platforms. (Linux, FreeBSD, Solaris...) */ #ifndef ifr_netmask #ifndef ifr_addr baseIPInfo->iae_mask = 0; ERR ("Unable to determine Netmask on your platform!\n"); #else struct ws_sockaddr_in *ipTemp = (struct ws_sockaddr_in *)&ifInfo.ifr_addr; baseIPInfo->iae_mask = ipTemp->sin_addr.S_un.S_addr; #endif #else struct ws_sockaddr_in *ipTemp = (struct ws_sockaddr_in *)&ifInfo.ifr_netmask; baseIPInfo->iae_mask = ipTemp->sin_addr.S_un.S_addr; #endif } /* 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); closesocket(sock); break; } case 0x101: FIXME ("Command ID Unknown but used by winipcfg.exe\n"); 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