2 * WSOCK32 specific functions
4 * Copyright (C) 1993,1994,1996,1997 John Brezak, Erik Bos, Alex Korobka.
12 #include "debugtools.h"
15 #include "wscontrol.h"
17 #include <sys/ioctl.h>
21 #ifdef HAVE_SYS_SOCKIO_H
22 # include <sys/sockio.h>
28 DEFAULT_DEBUG_CHANNEL(winsock);
31 /***********************************************************************
34 * WsControl seems to be an undocumented Win95 function. A lot of
35 * discussion about WsControl can be found on the net, e.g.
36 * Subject: Re: WSOCK32.DLL WsControl Exported Function
37 * From: "Peter Rindfuss" <rindfuss-s@medea.wz-berlin.de>
40 * WSCNTL_TCPIP_QUERY_INFO option is partially implemeted based
41 * on observing the behaviour of WsControl with an app in
42 * Windows 98. It is not fully implemented, and there could
43 * be (are?) errors due to incorrect assumptions made.
46 * WsControl returns WSCTL_SUCCESS on success.
47 * STATUS_BUFFER_TOO_SMALL is returned if the output buffer length
48 * (*pcbResponseInfoLen) is too small, otherwise errors return -1.
50 * It doesn't seem to generate errors that can be retrieved by
55 DWORD WINAPI WsControl(DWORD protocoll,
58 LPDWORD pcbRequestInfoLen,
60 LPDWORD pcbResponseInfoLen)
62 /* Get the command structure into a pointer we can use,
64 TDIObjectID *pcommand = (TDIObjectID *)pRequestInfo;
66 TRACE (" WsControl TOI_ID=>0x%lx<, {TEI_ENTITY=0x%lx, TEI_INSTANCE=0x%lx}, TOI_CLASS=0x%lx, TOI_TYPE=0x%lx\n",
67 pcommand->toi_id, pcommand->toi_entity.tei_entity, pcommand->toi_entity.tei_instance,
68 pcommand->toi_class, pcommand->toi_type );
74 case WSCNTL_TCPIP_QUERY_INFO:
76 switch (pcommand->toi_id)
79 ENTITY_LIST_ID seems to get number of adapters in the system.
80 (almost like an index to be used when calling other WsControl options)
84 TDIEntityID *baseptr = pResponseInfo;
87 if (pcommand->toi_class != INFO_CLASS_GENERIC &&
88 pcommand->toi_type != INFO_TYPE_PROVIDER)
90 FIXME ("Unexpected Option for ENTITY_LIST_ID request -> toi_class=0x%lx, toi_type=0x%lx\n",
91 pcommand->toi_class, pcommand->toi_type);
92 return (WSAEOPNOTSUPP);
95 numInt = WSCNTL_GetInterfaceCount();
98 ERR ("Unable to open /proc filesystem to determine number of network interfaces!\n");
102 if (*pcbResponseInfoLen < sizeof(TDIEntityID)*(numInt*2) )
104 return (STATUS_BUFFER_TOO_SMALL);
108 memset(baseptr, 0, sizeof(TDIEntityID)*(numInt*2));
110 for (i=0; i<numInt; i++)
112 /* tei_instance is an network interface identifier.
113 I'm not quite sure what the difference is between tei_entity values of
114 CL_NL_ENTITY and IF_ENTITY */
115 baseptr->tei_entity = CL_NL_ENTITY; baseptr->tei_instance = i; baseptr++;
116 baseptr->tei_entity = IF_ENTITY; baseptr->tei_instance = i; baseptr++;
119 /* Calculate size of out buffer */
120 *pcbResponseInfoLen = sizeof(TDIEntityID)*(numInt*2);
126 /* ENTITY_TYPE_ID is used to obtain simple information about a
127 network card, such as MAC Address, description, interface type,
128 number of network addresses, etc. */
129 case ENTITY_TYPE_ID: /* ALSO: IP_MIB_STATS_ID */
131 if (pcommand->toi_class == INFO_CLASS_GENERIC && pcommand->toi_type == INFO_TYPE_PROVIDER)
133 if (pcommand->toi_entity.tei_entity == IF_ENTITY)
135 * ((ULONG *)pResponseInfo) = IF_MIB;
137 /* Calculate size of out buffer */
138 *pcbResponseInfoLen = sizeof (ULONG);
141 else if (pcommand->toi_entity.tei_entity == CL_NL_ENTITY)
143 * ((ULONG *)pResponseInfo) = CL_NL_IP;
145 /* Calculate size of out buffer */
146 *pcbResponseInfoLen = sizeof (ULONG);
149 else if (pcommand->toi_class == INFO_CLASS_PROTOCOL &&
150 pcommand->toi_type == INFO_TYPE_PROVIDER)
152 if (pcommand->toi_entity.tei_entity == IF_ENTITY)
154 /* In this case, we are requesting specific information about a
155 a particular network adapter. (MAC Address, speed, data transmitted/received,
158 IFEntry *IntInfo = (IFEntry *) pResponseInfo;
164 if (!WSCNTL_GetInterfaceName(pcommand->toi_entity.tei_instance, ifName))
166 ERR ("Unable to parse /proc filesystem!\n");
170 /* Get a socket so that we can use ioctl */
171 if ( (sock = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
173 ERR ("Error creating socket!\n");
177 /* 0 out return structure first */
178 memset (IntInfo, 0, sizeof(IFEntry));
181 IntInfo->if_index = pcommand->toi_entity.tei_instance;
183 /* MAC Address - Let's try to do this in a cross-platform way... */
184 #if defined(SIOCGIFHWADDR) /* Linux */
185 strcpy(ifInfo.ifr_name, ifName);
186 if (ioctlsocket(sock, SIOCGIFHWADDR, (ULONG*)&ifInfo) < 0)
188 ERR ("Error obtaining MAC Address!\n");
194 /* FIXME: Is it correct to assume size of 6? */
195 memcpy(IntInfo->if_physaddr, ifInfo.ifr_hwaddr.sa_data, 6);
196 IntInfo->if_physaddrlen=6;
198 #elif defined(SIOCGENADDR) /* Solaris */
199 if (ioctlsocket(sock, SIOCGENADDR, (ULONG*)&ifInfo) < 0)
201 ERR ("Error obtaining MAC Address!\n");
207 /* FIXME: Is it correct to assume size of 6? */
208 memcpy(IntInfo->if_physaddr, ifInfo.ifr_enaddr, 6);
209 IntInfo->if_physaddrlen=6;
212 memset (IntInfo->if_physaddr, 0, 6);
213 ERR ("Unable to determine MAC Address on your platform!\n");
217 /* Interface name and length */
218 strcpy (IntInfo->if_descr, ifName);
219 IntInfo->if_descrlen= strlen (IntInfo->if_descr);
221 /* Obtain bytes transmitted/received for interface */
222 if ( (WSCNTL_GetTransRecvStat(pcommand->toi_entity.tei_instance,
223 &IntInfo->if_inoctets, &IntInfo->if_outoctets)) < 0)
225 ERR ("Error obtaining transmit/receive stats for the network interface!\n");
231 /* FIXME: How should the below be properly calculated? ******************/
232 IntInfo->if_type = 0x6; /* Ethernet (?) */
233 IntInfo->if_speed = 1000000; /* Speed of interface (bits per second?) */
234 /************************************************************************/
237 *pcbResponseInfoLen = sizeof (IFEntry) + IntInfo->if_descrlen;
239 else if (pcommand->toi_entity.tei_entity == CL_NL_ENTITY)
241 IPSNMPInfo *infoStruc = (IPSNMPInfo *) pResponseInfo;
244 /* This case is used to obtain general statistics about the
247 if (*pcbResponseInfoLen < sizeof(IPSNMPInfo) )
249 return (STATUS_BUFFER_TOO_SMALL);
254 memset(infoStruc, 0, sizeof(IPSNMPInfo));
256 /* Get the number of interfaces */
257 numInt = WSCNTL_GetInterfaceCount();
260 ERR ("Unable to open /proc filesystem to determine number of network interfaces!\n");
264 infoStruc->ipsi_numif = numInt; /* # of interfaces */
265 infoStruc->ipsi_numaddr = numInt; /* # of addresses */
266 infoStruc->ipsi_numroutes = numInt; /* # of routes ~ FIXME - Is this right? */
268 /* FIXME: How should the below be properly calculated? ******************/
269 infoStruc->ipsi_forwarding = 0x0;
270 infoStruc->ipsi_defaultttl = 0x0;
271 infoStruc->ipsi_inreceives = 0x0;
272 infoStruc->ipsi_inhdrerrors = 0x0;
273 infoStruc->ipsi_inaddrerrors = 0x0;
274 infoStruc->ipsi_forwdatagrams = 0x0;
275 infoStruc->ipsi_inunknownprotos = 0x0;
276 infoStruc->ipsi_indiscards = 0x0;
277 infoStruc->ipsi_indelivers = 0x0;
278 infoStruc->ipsi_outrequests = 0x0;
279 infoStruc->ipsi_routingdiscards = 0x0;
280 infoStruc->ipsi_outdiscards = 0x0;
281 infoStruc->ipsi_outnoroutes = 0x0;
282 infoStruc->ipsi_reasmtimeout = 0x0;
283 infoStruc->ipsi_reasmreqds = 0x0;
284 infoStruc->ipsi_reasmoks = 0x0;
285 infoStruc->ipsi_reasmfails = 0x0;
286 infoStruc->ipsi_fragoks = 0x0;
287 infoStruc->ipsi_fragfails = 0x0;
288 infoStruc->ipsi_fragcreates = 0x0;
289 /************************************************************************/
291 /* Calculate size of out buffer */
292 *pcbResponseInfoLen = sizeof(IPSNMPInfo);
298 FIXME ("Unexpected Option for ENTITY_TYPE_ID request -> toi_class=0x%lx, toi_type=0x%lx\n",
299 pcommand->toi_class, pcommand->toi_type);
301 return (WSAEOPNOTSUPP);
308 /* IP_MIB_ADDRTABLE_ENTRY_ID is used to obtain more detailed information about a
309 particular network adapter */
310 case IP_MIB_ADDRTABLE_ENTRY_ID:
312 IPAddrEntry *baseIPInfo = (IPAddrEntry *) pResponseInfo;
317 if (*pcbResponseInfoLen < sizeof(IPAddrEntry))
319 return (STATUS_BUFFER_TOO_SMALL);
322 if (!WSCNTL_GetInterfaceName(pcommand->toi_entity.tei_instance, ifName))
324 ERR ("Unable to parse /proc filesystem!\n");
329 /* Get a socket so we can use ioctl */
330 if ( (sock = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
332 ERR ("Error creating socket!\n");
337 memset(baseIPInfo, 0, sizeof(IPAddrEntry) );
340 baseIPInfo->iae_index = pcommand->toi_entity.tei_instance;
343 strcpy (ifInfo.ifr_name, ifName);
344 ifInfo.ifr_addr.sa_family = AF_INET;
345 if (ioctl(sock, SIOCGIFADDR, &ifInfo) < 0)
347 baseIPInfo->iae_addr = 0x0;
351 struct ws_sockaddr_in *ipTemp = (struct ws_sockaddr_in *)&ifInfo.ifr_addr;
352 baseIPInfo->iae_addr = ipTemp->sin_addr.S_un.S_addr;
355 /* Broadcast Address */
356 strcpy (ifInfo.ifr_name, ifName);
357 if (ioctl(sock, SIOCGIFBRDADDR, &ifInfo) < 0)
359 baseIPInfo->iae_bcastaddr = 0x0;
363 struct ws_sockaddr_in *ipTemp = (struct ws_sockaddr_in *)&ifInfo.ifr_broadaddr;
364 baseIPInfo->iae_bcastaddr = ipTemp->sin_addr.S_un.S_addr;
368 strcpy(ifInfo.ifr_name, ifName);
369 if (ioctl(sock, SIOCGIFNETMASK, &ifInfo) < 0)
371 baseIPInfo->iae_mask = 0x0;
375 /* Trying to avoid some compile problems across platforms.
376 (Linux, FreeBSD, Solaris...) */
379 baseIPInfo->iae_mask = 0;
380 ERR ("Unable to determine Netmask on your platform!\n");
382 struct ws_sockaddr_in *ipTemp = (struct ws_sockaddr_in *)&ifInfo.ifr_addr;
383 baseIPInfo->iae_mask = ipTemp->sin_addr.S_un.S_addr;
386 struct ws_sockaddr_in *ipTemp = (struct ws_sockaddr_in *)&ifInfo.ifr_netmask;
387 baseIPInfo->iae_mask = ipTemp->sin_addr.S_un.S_addr;
391 /* FIXME: How should the below be properly calculated? ******************/
392 baseIPInfo->iae_reasmsize = 0x0;
393 baseIPInfo->iae_context = 0x0;
394 baseIPInfo->iae_pad = 0x0;
395 /************************************************************************/
397 /* Calculate size of out buffer */
398 *pcbResponseInfoLen = sizeof(IPAddrEntry);
406 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",
407 pcommand->toi_id, pcommand->toi_entity.tei_entity, pcommand->toi_entity.tei_instance,
408 pcommand->toi_class, pcommand->toi_type);
410 return (WSAEOPNOTSUPP);
417 case WSCNTL_TCPIP_ICMP_ECHO:
419 unsigned int addr = *(unsigned int*)pRequestInfo;
421 int timeout= *(unsigned int*)(inbuf+4);
422 short x1 = *(unsigned short*)(inbuf+8);
423 short sendbufsize = *(unsigned short*)(inbuf+10);
424 char x2 = *(unsigned char*)(inbuf+12);
425 char ttl = *(unsigned char*)(inbuf+13);
426 char service = *(unsigned char*)(inbuf+14);
427 char type= *(unsigned char*)(inbuf+15); /* 0x2: don't fragment*/
430 FIXME("(ICMP_ECHO) to 0x%08x stub \n", addr);
436 FIXME("Protocoll Not Supported -> protocoll=0x%lx, action=0x%lx, Request=%p, RequestLen=%p, Response=%p, ResponseLen=%p\n",
437 protocoll, action, pRequestInfo, pcbRequestInfoLen, pResponseInfo, pcbResponseInfoLen);
439 return (WSAEOPNOTSUPP);
444 return (WSCTL_SUCCESS);
450 Helper function for WsControl - Get count of the number of interfaces
451 by parsing /proc filesystem.
453 int WSCNTL_GetInterfaceCount(void)
456 char buf[512]; /* Size doesn't matter, something big */
460 /* Open /proc filesystem file for network devices */
461 procfs = fopen(PROCFS_NETDEV_FILE, "r");
464 /* If we can't open the file, return an error */
468 /* Omit first two lines, they are only headers */
469 fgets(buf, sizeof buf, procfs);
470 fgets(buf, sizeof buf, procfs);
472 while (fgets(buf, sizeof buf, procfs))
474 /* Each line in the file represents a network interface */
484 Helper function for WsControl - Get name of device from interface number
485 by parsing /proc filesystem.
487 int WSCNTL_GetInterfaceName(int intNumber, char *intName)
490 char buf[512]; /* Size doesn't matter, something big */
493 /* Open /proc filesystem file for network devices */
494 procfs = fopen(PROCFS_NETDEV_FILE, "r");
497 /* If we can't open the file, return an error */
501 /* Omit first two lines, they are only headers */
502 fgets(buf, sizeof(buf), procfs);
503 fgets(buf, sizeof(buf), procfs);
505 for (i=0; i<intNumber; i++)
507 /* Skip the lines that don't interest us. */
508 fgets(buf, sizeof(buf), procfs);
510 fgets(buf, sizeof(buf), procfs); /* This is the line we want */
513 /* Parse out the line, grabbing only the name of the device
514 to the intName variable
516 The Line comes in like this: (we only care about the device name)
517 lo: 21970 377 0 0 0 0 0 0 21970 377 0 0 0 0 0 0
520 while (isspace(buf[i])) /* Skip initial space(s) */
532 if (buf[i] == ':') /* FIXME: Not sure if this block (alias detection) works properly */
534 /* This interface could be an alias... */
536 char *dotname = intName;
537 *intName++ = buf[i++];
539 while (isdigit(buf[i]))
541 *intName++ = buf[i++];
546 /* ... It wasn't, so back up */
561 *intName++ = buf[i++];
571 Helper function for WsControl - This function returns the bytes (octets) transmitted
572 and received for the supplied interface number from the /proc fs.
574 int WSCNTL_GetTransRecvStat(int intNumber, unsigned long *transBytes, unsigned long *recvBytes)
577 char buf[512], result[512]; /* Size doesn't matter, something big */
578 int i, bufPos, resultPos;
580 /* Open /proc filesystem file for network devices */
581 procfs = fopen(PROCFS_NETDEV_FILE, "r");
584 /* If we can't open the file, return an error */
588 /* Omit first two lines, they are only headers */
589 fgets(buf, sizeof(buf), procfs);
590 fgets(buf, sizeof(buf), procfs);
592 for (i=0; i<intNumber; i++)
594 /* Skip the lines that don't interest us. */
595 fgets(buf, sizeof(buf), procfs);
597 fgets(buf, sizeof(buf), procfs); /* This is the line we want */
601 /* Parse out the line, grabbing the number of bytes transmitted
602 and received on the interface.
604 The Line comes in like this: (we care about columns 2 and 10)
605 lo: 21970 377 0 0 0 0 0 0 21970 377 0 0 0 0 0 0
608 /* Start at character 0 in the buffer */
611 /* Skip initial space(s) */
612 while (isspace(buf[bufPos]))
616 /* Skip the name and its trailing spaces (if any) */
619 if (isspace(buf[bufPos]))
622 if (buf[bufPos] == ':') /* Could be an alias */
626 while(isdigit (buf[bufPos]))
628 if (buf[bufPos] != ':')
630 if (buf[bufPos] == '\0')
642 while (isspace(buf[bufPos]))
646 /* This column (#2) is the number of bytes received. */
648 while (!isspace(buf[bufPos]))
650 result[resultPos] = buf[bufPos];
651 result[resultPos+1]='\0';
652 resultPos++; bufPos++;
654 *recvBytes = strtoul (result, NULL, 10); /* convert string to unsigned long, using base 10 */
657 /* Skip columns #3 to #9 (Don't need them) */
660 while (isspace(buf[bufPos]))
662 while (!isspace(buf[bufPos]))
667 /* This column (#10) is the number of bytes transmitted */
668 while (isspace(buf[bufPos]))
672 while (!isspace(buf[bufPos]))
674 result[resultPos] = buf[bufPos];
675 result[resultPos+1]='\0';
676 resultPos++; bufPos++;
678 *transBytes = strtoul (result, NULL, 10); /* convert string to unsigned long, using base 10 */
686 /***********************************************************************
687 * WSARecvEx() (WSOCK32.1107)
689 * WSARecvEx is a Microsoft specific extension to winsock that is identical to recv
690 * except that has an in/out argument call flags that has the value MSG_PARTIAL ored
691 * into the flags parameter when a partial packet is read. This only applies to
692 * sockets using the datagram protocol. This method does not seem to be implemented
693 * correctly by microsoft as the winsock implementation does not set the MSG_PARTIAL
694 * flag when a fragmented packet arrives.
696 INT WINAPI WSARecvEx(SOCKET s, char *buf, INT len, INT *flags)
698 FIXME("(WSARecvEx) partial packet return value not set \n");
699 return recv(s, buf, len, *flags);
703 /***********************************************************************
704 * WS_s_perror (WSOCK32.1108)
706 void WINAPI WS_s_perror(LPCSTR message)
708 FIXME("(%s): stub\n",message);