/* * WSOCK32 specific functions * * Copyright (C) 1993,1994,1996,1997 John Brezak, Erik Bos, Alex Korobka. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* FIXME: This hack is fixing a problem in WsControl. When we call socket(), * it will call into ws2_32's WSOCK32_socket (because of the redirection in * our own .spec file). * 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. * Furthermore WsControl uses Unix macros and types. This forces us to include * the Unix headers which then conflict with the winsock headers. This forces * us to use USE_WS_PREFIX but then ioctlsocket is called WS_ioctlsocket, * which causes link problems. The correct solution is to implement * WsControl using calls to WSAIoctl. Then we should no longer need to use the * Unix headers. This would also have the advantage of reducing code * duplication. * Until that happens we need this ugly hack. */ #define USE_WS_PREFIX #define socket linux_socket #define recv linux_recv /* */ #include "config.h" #include #include #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_SYS_IOCTL_H # include #endif #include #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_SYS_SOCKIO_H # include #endif #ifdef HAVE_NET_IF_H # include #endif #include "windef.h" #include "winbase.h" #include "wine/debug.h" #include "winsock2.h" #include "winnt.h" #include "wscontrol.h" /* FIXME: The rest of the socket() cdecl<->stdapi stack corruption problem * discussed above. */ #undef socket #undef recv extern SOCKET WINAPI socket(INT af, INT type, INT protocol); extern SOCKET WINAPI recv(SOCKET,char*,int,int); /* Plus some missing prototypes, due to the WS_ prefixing */ extern int WINAPI closesocket(SOCKET); extern int WINAPI ioctlsocket(SOCKET,long,u_long*); /* */ WINE_DEFAULT_DEBUG_CHANNEL(winsock); /*********************************************************************** * WsControl (WSOCK32.1001) * * 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]; #if defined(SIOCGIFHWADDR) || defined(SIOCGENADDR) struct ifreq ifInfo; #endif 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 entries!\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); } /*********************************************************************** * s_perror (WSOCK32.1108) */ void WINAPI s_perror(LPCSTR message) { FIXME("(%s): stub\n",message); return; }