/* * 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 #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 #include #ifdef __NetBSD__ #undef if_type #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_GetEntryCount(WSCNTL_COUNT_INTERFACES); 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)) == INVALID_SOCKET) { 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, numRoutes; /* 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_GetEntryCount(WSCNTL_COUNT_INTERFACES); if (numInt < 0) { ERR ("Unable to open /proc filesystem to determine number of network interfaces!\n"); return (-1); } /* Get the number of routes */ numRoutes = WSCNTL_GetEntryCount(WSCNTL_COUNT_ROUTES); if (numRoutes < 0) { ERR ("Unable to open /proc filesystem to determine number of network routes!\n"); return (-1); } infoStruc->ipsi_numif = numInt; /* # of interfaces */ infoStruc->ipsi_numaddr = numInt; /* # of addresses */ infoStruc->ipsi_numroutes = numRoutes; /* # of routes */ /* 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[IFNAMSIZ+1]; 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)) == INVALID_SOCKET) { 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; } /* 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 IP_MIB_ROUTETABLE_ENTRY_ID: /* FIXME: not real name. Value is 0x101 */ { int numRoutes, foundRoutes; wscntl_routeentry *routeTable, *routePtr; /* route table */ IPRouteEntry *winRouteTable = (IPRouteEntry *) pResponseInfo; /* Get the number of routes */ numRoutes = WSCNTL_GetEntryCount(WSCNTL_COUNT_ROUTES); if (numRoutes < 0) { ERR ("Unable to open /proc filesystem to determine number of network routes!\n"); return (-1); } if (*pcbResponseInfoLen < (sizeof(IPRouteEntry) * numRoutes)) { return (STATUS_BUFFER_TOO_SMALL); } /* malloc space for the routeTable */ routeTable = (wscntl_routeentry *) malloc(sizeof(wscntl_routeentry) * numRoutes); if (!routeTable) { ERR ("couldn't malloc space for routeTable!\n"); } /* get the route table */ foundRoutes = WSCNTL_GetRouteTable(numRoutes, routeTable); if (foundRoutes < 0) { ERR ("Unable to open /proc filesystem to parse the route entrys!\n"); free(routeTable); return -1; } routePtr = routeTable; /* first 0 out the output buffer */ memset(winRouteTable, 0, *pcbResponseInfoLen); /* calculate the length of the data in the output buffer */ *pcbResponseInfoLen = sizeof(IPRouteEntry) * foundRoutes; for ( ; foundRoutes > 0; foundRoutes--) { winRouteTable->ire_addr = routePtr->wre_dest; winRouteTable->ire_index = routePtr->wre_intf; winRouteTable->ire_metric = routePtr->wre_metric; /* winRouteTable->ire_option4 = winRouteTable->ire_option5 = winRouteTable->ire_option6 = */ winRouteTable->ire_gw = routePtr->wre_gw; /* winRouteTable->ire_option8 = winRouteTable->ire_option9 = winRouteTable->ire_option10 = */ winRouteTable->ire_mask = routePtr->wre_mask; /* winRouteTable->ire_option12 = */ winRouteTable++; routePtr++; } free(routeTable); 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 or routes by parsing /proc filesystem. */ int WSCNTL_GetEntryCount(const int entrytype) { char *filename; int fd; char buf[512]; /* Size optimized for a typical workstation */ char *ptr; int count; int chrread; switch (entrytype) { case WSCNTL_COUNT_INTERFACES: { filename = PROCFS_NETDEV_FILE; count = -2; /* two haeder lines */ break; }; case WSCNTL_COUNT_ROUTES: { filename = PROCFS_ROUTE_FILE; count = -1; /* one haeder line */ break; }; default: { return -1; }; } /* open /proc filesystem file */ fd = open(filename, O_RDONLY); if (fd < 0) { return -1; } /* read the file and count the EOL's */ while ((chrread = read(fd, buf, sizeof(buf))) != 0) { ptr = buf; if (chrread < 0) { if (errno == EINTR) { continue; /* read interupted by a signal, try to read again */ } else { close(fd); return -1; } } while ((ptr = memchr(ptr, '\n', chrread - (int) (ptr - buf))) > 0) { count++; ptr++; } } close(fd); return count; } /* 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 numRoutes) { /* output buffer is to small */ ERR("buffer to small to fit all routes found into it!\n"); free(interface); fclose(file); return -1; } ptr = buf; ptr += interface[intfNr].intfNameLen; routePtr->wre_intf = intfNr; routePtr->wre_dest = strtoul(ptr, &ptr, 16); /* destination */ routePtr->wre_gw = strtoul(ptr, &ptr, 16); /* gateway */ strtoul(ptr, &ptr, 16); /* Flags; unused */ strtoul(ptr, &ptr, 16); /* RefCnt; unused */ strtoul(ptr, &ptr, 16); /* Use; unused */ routePtr->wre_metric = strtoul(ptr, &ptr, 16); /* metric */ routePtr->wre_mask = strtoul(ptr, &ptr, 16); /* mask */ /* strtoul(ptr, &ptr, 16); MTU; unused */ /* strtoul(ptr, &ptr, 16); Window; unused */ /* strtoul(ptr, &ptr, 16); IRTT; unused */ routePtr++; } else { /* this should never happen */ WARN("Skipping route with unknown interface\n"); } } free(interface); fclose(file); return foundRoutes; } /*********************************************************************** * 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); } /*********************************************************************** * WS_s_perror (WSOCK32.1108) */ void WINAPI WS_s_perror(LPCSTR message) { FIXME("(%s): stub\n",message); return; }