/*
 * 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 <errno.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>

#include <sys/types.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_SYS_SOCKIO_H
# include <sys/sockio.h>
#endif
#ifdef HAVE_NET_IF_H
# include <net/if.h>
#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" <rindfuss-s@medea.wz-berlin.de>
 * 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; i<numInt; i++)
               {
                  /* tei_instance is an network interface identifier.
                     I'm not quite sure what the difference is between tei_entity values of 
                     CL_NL_ENTITY and IF_ENTITY */
                  baseptr->tei_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<intNumber; i++)
   {
      /* Skip the lines that don't interest us. */
      fgets(buf, sizeof(buf), procfs);
   }
   fgets(buf, sizeof(buf), procfs); /* This is the line we want */

   
   /* Parse out the line, grabbing only the name of the device
      to the intName variable 
      
      The Line comes in like this: (we only care about the device name)
      lo:   21970 377 0 0 0 0 0 0 21970 377 0 0 0 0 0 0
   */
   i=0; 
   while (isspace(buf[i])) /* Skip initial space(s) */
   {
      i++;
   }

   while (buf[i]) 
   {
      if (isspace(buf[i]))
      {
         break;
      }
      
      if (buf[i] == ':')  /* FIXME: Not sure if this block (alias detection) works properly */
      {
         /* This interface could be an alias... */
         int hold = i;
         char *dotname = intName;
         *intName++ = buf[i++];
         
         while (isdigit(buf[i]))
         {
            *intName++ = buf[i++];
         }
         
         if (buf[i] != ':') 
         {
            /* ... It wasn't, so back up */
            i = hold;
            intName = dotname;
         }
 
         if (buf[i] == '\0')
         {
            fclose(procfs);
            return(FALSE);
         }
         
         i++;
         break;
      }
      
      *intName++ = buf[i++];
   }
   *intName++ = '\0';

   fclose(procfs);
   return(TRUE);
}


/*
   Helper function for WsControl - This function returns the bytes (octets) transmitted
   and received for the supplied interface number from the /proc fs. 
*/
int WSCNTL_GetTransRecvStat(int intNumber, unsigned long *transBytes, unsigned long *recvBytes)
{
   FILE *procfs;
   char buf[512], result[512]; /* Size doesn't matter, something big */
   int  i, bufPos, resultPos;

   /* 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<intNumber; i++)
   {
      /* Skip the lines that don't interest us. */
      fgets(buf, sizeof(buf), procfs);
   }
   fgets(buf, sizeof(buf), procfs); /* This is the line we want */



   /* Parse out the line, grabbing the number of bytes transmitted
      and received on the interface.
      
      The Line comes in like this: (we care about columns 2 and 10)
      lo:   21970 377 0 0 0 0 0 0 21970 377 0 0 0 0 0 0
   */

   /* Start at character 0 in the buffer */
   bufPos=0;
   
   /* Skip initial space(s) */ 
   while (isspace(buf[bufPos])) 
      bufPos++;


   /* Skip the name and its trailing spaces (if any) */
   while (buf[bufPos]) 
   {
      if (isspace(buf[bufPos]))
         break;
 
      if (buf[bufPos] == ':') /* Could be an alias */
      {
         int hold = bufPos;

         while(isdigit (buf[bufPos]))
            bufPos++;
         if (buf[bufPos] != ':')
            bufPos = hold;
         if (buf[bufPos] == '\0')
         {
            fclose(procfs);
            return(FALSE);
         }
         
         bufPos++;
         break;
      }

      bufPos++;
   }
   while (isspace(buf[bufPos]))
      bufPos++;


   /* This column (#2) is the number of bytes received. */
   resultPos = 0;
   while (!isspace(buf[bufPos]))
   {
      result[resultPos] = buf[bufPos];
      result[resultPos+1]='\0';
      resultPos++; bufPos++;
   }
   *recvBytes = strtoul (result, NULL, 10); /* convert string to unsigned long, using base 10 */

   
   /* Skip columns #3 to #9 (Don't need them) */
   for  (i=0; i<7; i++)
   {
      while (isspace(buf[bufPos]))
         bufPos++;
      while (!isspace(buf[bufPos])) 
         bufPos++;
   }


   /* This column (#10) is the number of bytes transmitted */
   while (isspace(buf[bufPos]))
       bufPos++;

   resultPos = 0;
   while (!isspace(buf[bufPos]))
   {
      result[resultPos] = buf[bufPos];
      result[resultPos+1]='\0';
      resultPos++; bufPos++;
   }
   *transBytes = strtoul (result, NULL, 10); /* convert string to unsigned long, using base 10 */


   fclose(procfs);
   return(TRUE);
}


/* Parse the procfs route file and put the datas into routeTable.
 * Return value is the number of found routes */
int WSCNTL_GetRouteTable(int numRoutes, wscntl_routeentry *routeTable)
{
    int nrIntf;		/* total number of interfaces */
    char buf[256];	/* temporary buffer */
    char *ptr;		/* pointer to temporary buffer */
    FILE *file;		/* file handle for procfs route file */
    int foundRoutes = 0;	/* number of found routes */
    typedef struct interface_t {
	char intfName[IFNAMSIZ+1];	/* the name of the interface */
	int intfNameLen;	/* length of interface name */
    } interface_t;
    interface_t *interface;
    int intfNr;		/* the interface number */

    wscntl_routeentry *routePtr = routeTable;

    /* get the number of interfaces */
    nrIntf = WSCNTL_GetEntryCount(WSCNTL_COUNT_INTERFACES);
    if (nrIntf < 0)
    {
	ERR ("Unable to open /proc filesystem to determine number of network interfaces!\n");
	return (-1);
    }

    /* malloc space for the interface struct array */
    interface = (interface_t *) malloc(sizeof(interface_t) * nrIntf);
    if (!routeTable)
    {
	ERR ("couldn't malloc space for interface!\n");
    }

    for (intfNr = 0; intfNr < nrIntf; intfNr++) {
	if (WSCNTL_GetInterfaceName(intfNr, interface[intfNr].intfName) < 0)
	{
	    ERR ("Unable to open /proc filesystem to determine the name of network interfaces!\n");
	    free(interface);
	    return (-1);
	}
	interface[intfNr].intfNameLen = strlen(interface[intfNr].intfName);
    }

    /* Open /proc filesystem file for routes */ 
    file = fopen(PROCFS_ROUTE_FILE, "r");
    if (!file) 
    {
       	/* If we can't open the file, return an error */
	free(interface);
	return (-1);
    }
    
    /* skip the header line */
    fgets(buf, sizeof(buf), file);

    /* parse the rest of the file and put the matching entries into routeTable.
       Format of procfs route entry:
       Iface Destination Gateway Flags RefCnt Use Metric Mask  MTU Window IRTT
       lo 0000007F 00000000 0001 0 0 0 000000FF 0 0 0
    */
    while (fgets(buf, sizeof(buf), file)) {
	intfNr = 0;
	/* find the interface of the route */
	while ((strncmp(buf, interface[intfNr].intfName, interface[intfNr].intfNameLen) != 0) 
		&& (intfNr < nrIntf))
	{
	    intfNr++;
	}
	if (intfNr < nrIntf) {
	    foundRoutes++;
	    if (foundRoutes > 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;
}
