Implemented WsControl command with id 0x101.
[wine] / dlls / wsock32 / socket.c
1 /*
2  * WSOCK32 specific functions
3  *
4  * Copyright (C) 1993,1994,1996,1997 John Brezak, Erik Bos, Alex Korobka.
5  */
6
7
8 /*
9   FIXME: This hack is fixing a problem in WsControl.  When we call socket(),
10          it is supposed to call into ws2_32's WSOCK32_socket.
11          The problem is that socket() is predefined in a linux system header that 
12          we are including, which is different from the WINE definition.
13          (cdecl vs. stdapi)  The result is stack corruption.
14
15          The correct answer to this problem is to make winsock.h not dependent 
16          on system headers, that way all of our functions are defined consistently.
17          Until that happens we need this hack.
18 */
19 #define socket  linux_socket
20 /* */
21
22 #include "config.h"
23
24 #include <string.h>
25 #include <sys/types.h>
26 #include "windef.h"
27 #include "winbase.h"
28 #include "debugtools.h"
29 #include "winsock2.h"
30 #include "winnt.h"
31 #include "wscontrol.h"
32 #include <ctype.h>
33 #include <sys/ioctl.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #ifdef HAVE_SYS_SOCKIO_H
38 # include <sys/sockio.h>
39 #endif
40 #ifdef HAVE_NET_IF_H
41 # include <net/if.h>
42 #endif
43 #include <errno.h>
44
45
46 /* FIXME: The rest of the socket() cdecl<->stdapi stack corruption problem
47           discussed above. */
48 #undef socket
49 extern SOCKET WINAPI socket(INT af, INT type, INT protocol);
50 /* */
51
52
53 DEFAULT_DEBUG_CHANNEL(winsock);
54
55
56 /***********************************************************************
57  *      WsControl()
58  *
59  * WsControl seems to be an undocumented Win95 function. A lot of 
60  * discussion about WsControl can be found on the net, e.g.
61  * Subject:      Re: WSOCK32.DLL WsControl Exported Function
62  * From:         "Peter Rindfuss" <rindfuss-s@medea.wz-berlin.de>
63  * Date:         1997/08/17
64  *
65  * WSCNTL_TCPIP_QUERY_INFO option is partially implemeted based
66  * on observing the behaviour of WsControl with an app in 
67  * Windows 98.  It is not fully implemented, and there could
68  * be (are?) errors due to incorrect assumptions made.
69  *
70  *
71  * WsControl returns WSCTL_SUCCESS on success.
72  * STATUS_BUFFER_TOO_SMALL is returned if the output buffer length
73  * (*pcbResponseInfoLen) is too small, otherwise errors return -1.
74  *
75  * It doesn't seem to generate errors that can be retrieved by 
76  * WSAGetLastError().
77  *
78  */
79
80 DWORD WINAPI WsControl(DWORD protocoll,
81                        DWORD action,
82                        LPVOID pRequestInfo,
83                        LPDWORD pcbRequestInfoLen,
84                        LPVOID pResponseInfo,
85                        LPDWORD pcbResponseInfoLen) 
86 {
87    /* Get the command structure into a pointer we can use,
88       rather than void */
89    TDIObjectID *pcommand = (TDIObjectID *)pRequestInfo;
90   
91    TRACE ("   WsControl TOI_ID=>0x%lx<, {TEI_ENTITY=0x%lx, TEI_INSTANCE=0x%lx}, TOI_CLASS=0x%lx, TOI_TYPE=0x%lx\n",
92           pcommand->toi_id, pcommand->toi_entity.tei_entity, pcommand->toi_entity.tei_instance,
93           pcommand->toi_class, pcommand->toi_type );
94   
95
96
97    switch (action) 
98    {
99       case WSCNTL_TCPIP_QUERY_INFO: 
100       {
101          switch (pcommand->toi_id)
102          {
103             /* 
104                ENTITY_LIST_ID seems to get number of adapters in the system.
105                (almost like an index to be used when calling other WsControl options)
106             */
107             case ENTITY_LIST_ID: 
108             {
109                TDIEntityID *baseptr = pResponseInfo;
110                int numInt = 0, i;
111
112                if (pcommand->toi_class != INFO_CLASS_GENERIC &&
113                    pcommand->toi_type != INFO_TYPE_PROVIDER) 
114                { 
115                   FIXME ("Unexpected Option for ENTITY_LIST_ID request -> toi_class=0x%lx, toi_type=0x%lx\n",
116                        pcommand->toi_class, pcommand->toi_type);
117                   return (WSAEOPNOTSUPP); 
118                }
119            
120                numInt = WSCNTL_GetEntryCount(WSCNTL_COUNT_INTERFACES); 
121                if (numInt < 0)
122                {
123                   ERR ("Unable to open /proc filesystem to determine number of network interfaces!\n");
124                   return (-1); 
125                }
126
127                if (*pcbResponseInfoLen < sizeof(TDIEntityID)*(numInt*2) ) 
128                {
129                   return (STATUS_BUFFER_TOO_SMALL);
130                }
131            
132                /* 0 it out first */
133                memset(baseptr, 0, sizeof(TDIEntityID)*(numInt*2)); 
134                
135                for (i=0; i<numInt; i++)
136                {
137                   /* tei_instance is an network interface identifier.
138                      I'm not quite sure what the difference is between tei_entity values of 
139                      CL_NL_ENTITY and IF_ENTITY */
140                   baseptr->tei_entity = CL_NL_ENTITY;  baseptr->tei_instance = i; baseptr++;
141                   baseptr->tei_entity = IF_ENTITY;     baseptr->tei_instance = i; baseptr++; 
142                }
143
144                /* Calculate size of out buffer */
145                *pcbResponseInfoLen = sizeof(TDIEntityID)*(numInt*2);
146             
147                break;
148             }
149           
150           
151             /* ENTITY_TYPE_ID is used to obtain simple information about a 
152                network card, such as MAC Address, description, interface type,
153                number of network addresses, etc. */
154             case ENTITY_TYPE_ID:  /* ALSO: IP_MIB_STATS_ID */
155             {
156                if (pcommand->toi_class == INFO_CLASS_GENERIC && pcommand->toi_type == INFO_TYPE_PROVIDER) 
157                {
158                   if (pcommand->toi_entity.tei_entity == IF_ENTITY)
159                   {
160                      * ((ULONG *)pResponseInfo) = IF_MIB; 
161
162                      /* Calculate size of out buffer */
163                      *pcbResponseInfoLen = sizeof (ULONG);
164
165                   }
166                   else if (pcommand->toi_entity.tei_entity == CL_NL_ENTITY) 
167                   {
168                      * ((ULONG *)pResponseInfo) = CL_NL_IP;  
169
170                      /* Calculate size of out buffer */
171                      *pcbResponseInfoLen = sizeof (ULONG); 
172                   }
173                }
174                else if (pcommand->toi_class == INFO_CLASS_PROTOCOL &&
175                         pcommand->toi_type == INFO_TYPE_PROVIDER)
176                {
177                   if (pcommand->toi_entity.tei_entity == IF_ENTITY)
178                   {
179                      /* In this case, we are requesting specific information about a 
180                         a particular network adapter. (MAC Address, speed, data transmitted/received,
181                         etc.)
182                      */ 
183                      IFEntry *IntInfo = (IFEntry *) pResponseInfo;
184                      char ifName[512];
185                      struct ifreq ifInfo;
186                      SOCKET sock;
187
188                      
189                      if (!WSCNTL_GetInterfaceName(pcommand->toi_entity.tei_instance, ifName))
190                      {
191                         ERR ("Unable to parse /proc filesystem!\n");
192                         return (-1);
193                      }
194                
195                      /* Get a socket so that we can use ioctl */
196                      if ( (sock = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
197                      {
198                         ERR ("Error creating socket!\n");
199                         return (-1);
200                      }
201                    
202                      /* 0 out return structure first */
203                      memset (IntInfo, 0, sizeof(IFEntry));
204                      
205                      /* Interface ID */
206                      IntInfo->if_index = pcommand->toi_entity.tei_instance;
207                      
208                      /* MAC Address - Let's try to do this in a cross-platform way... */
209                      #if defined(SIOCGIFHWADDR) /* Linux */
210                         strcpy(ifInfo.ifr_name, ifName);
211                         if (ioctlsocket(sock, SIOCGIFHWADDR, (ULONG*)&ifInfo) < 0)
212                         {
213                            ERR ("Error obtaining MAC Address!\n");
214                            closesocket(sock);
215                            return (-1);
216                         }
217                         else
218                         {
219                            /* FIXME: Is it correct to assume size of 6? */
220                            memcpy(IntInfo->if_physaddr, ifInfo.ifr_hwaddr.sa_data, 6);
221                            IntInfo->if_physaddrlen=6;
222                         }
223                      #elif defined(SIOCGENADDR) /* Solaris */
224                         if (ioctlsocket(sock, SIOCGENADDR, (ULONG*)&ifInfo) < 0)
225                         {
226                            ERR ("Error obtaining MAC Address!\n");
227                            closesocket(sock);
228                            return (-1);
229                         }
230                         else
231                         {
232                            /* FIXME: Is it correct to assume size of 6? */
233                            memcpy(IntInfo->if_physaddr, ifInfo.ifr_enaddr, 6);
234                            IntInfo->if_physaddrlen=6;
235                         }
236                      #else
237                         memset (IntInfo->if_physaddr, 0, 6);
238                         ERR ("Unable to determine MAC Address on your platform!\n");
239                      #endif
240
241                      
242                      /* Interface name and length */
243                      strcpy (IntInfo->if_descr, ifName);
244                      IntInfo->if_descrlen= strlen (IntInfo->if_descr);
245                      
246                      /* Obtain bytes transmitted/received for interface */
247                      if ( (WSCNTL_GetTransRecvStat(pcommand->toi_entity.tei_instance, 
248                            &IntInfo->if_inoctets, &IntInfo->if_outoctets)) < 0)
249                      {
250                         ERR ("Error obtaining transmit/receive stats for the network interface!\n");
251                         closesocket(sock);
252                         return (-1);
253                      }
254                      
255                      
256                      /* FIXME: How should the below be properly calculated? ******************/
257                      IntInfo->if_type =  0x6; /* Ethernet (?) */
258                      IntInfo->if_speed = 1000000; /* Speed of interface (bits per second?) */
259                      /************************************************************************/
260
261                      closesocket(sock);
262                      *pcbResponseInfoLen = sizeof (IFEntry) + IntInfo->if_descrlen; 
263                   }
264                   else if (pcommand->toi_entity.tei_entity == CL_NL_ENTITY) 
265                   {
266                      IPSNMPInfo *infoStruc = (IPSNMPInfo *) pResponseInfo;
267                      int numInt, numRoutes;
268                      
269                      /* This case is used to obtain general statistics about the 
270                         network */
271                      
272                      if (*pcbResponseInfoLen < sizeof(IPSNMPInfo) )
273                      {
274                         return (STATUS_BUFFER_TOO_SMALL);
275                      }
276                      else
277                      {
278                         /* 0 it out first */
279                         memset(infoStruc, 0, sizeof(IPSNMPInfo));
280             
281                         /* Get the number of interfaces */
282                         numInt = WSCNTL_GetEntryCount(WSCNTL_COUNT_INTERFACES); 
283                         if (numInt < 0)
284                         {
285                            ERR ("Unable to open /proc filesystem to determine number of network interfaces!\n");
286                            return (-1); 
287                         }
288                         /* Get the number of routes */
289                         numRoutes = WSCNTL_GetEntryCount(WSCNTL_COUNT_ROUTES); 
290                         if (numRoutes < 0)
291                         {
292                            ERR ("Unable to open /proc filesystem to determine number of network routes!\n");
293                            return (-1); 
294                         }
295
296                         infoStruc->ipsi_numif           = numInt; /* # of interfaces */
297                         infoStruc->ipsi_numaddr         = numInt; /* # of addresses */
298                         infoStruc->ipsi_numroutes       = numRoutes; /* # of routes */
299
300                         /* FIXME: How should the below be properly calculated? ******************/
301                         infoStruc->ipsi_forwarding      = 0x0;
302                         infoStruc->ipsi_defaultttl      = 0x0;
303                         infoStruc->ipsi_inreceives      = 0x0;
304                         infoStruc->ipsi_inhdrerrors     = 0x0;
305                         infoStruc->ipsi_inaddrerrors    = 0x0;
306                         infoStruc->ipsi_forwdatagrams   = 0x0;
307                         infoStruc->ipsi_inunknownprotos = 0x0;
308                         infoStruc->ipsi_indiscards      = 0x0;
309                         infoStruc->ipsi_indelivers      = 0x0;
310                         infoStruc->ipsi_outrequests     = 0x0;
311                         infoStruc->ipsi_routingdiscards = 0x0;
312                         infoStruc->ipsi_outdiscards     = 0x0;
313                         infoStruc->ipsi_outnoroutes     = 0x0;
314                         infoStruc->ipsi_reasmtimeout    = 0x0;
315                         infoStruc->ipsi_reasmreqds      = 0x0;
316                         infoStruc->ipsi_reasmoks        = 0x0;
317                         infoStruc->ipsi_reasmfails      = 0x0;
318                         infoStruc->ipsi_fragoks         = 0x0;
319                         infoStruc->ipsi_fragfails       = 0x0;
320                         infoStruc->ipsi_fragcreates     = 0x0;
321                         /************************************************************************/
322                       
323                         /* Calculate size of out buffer */
324                         *pcbResponseInfoLen = sizeof(IPSNMPInfo);
325                      }
326                   }
327                }
328                else
329                {
330                   FIXME ("Unexpected Option for ENTITY_TYPE_ID request -> toi_class=0x%lx, toi_type=0x%lx\n",
331                        pcommand->toi_class, pcommand->toi_type);
332                   
333                   return (WSAEOPNOTSUPP); 
334                }
335
336                break;
337             }
338
339
340             /* IP_MIB_ADDRTABLE_ENTRY_ID is used to obtain more detailed information about a 
341                particular network adapter */
342             case IP_MIB_ADDRTABLE_ENTRY_ID: 
343             {
344                IPAddrEntry *baseIPInfo = (IPAddrEntry *) pResponseInfo;
345                char ifName[IFNAMSIZ+1];
346                struct ifreq ifInfo;
347                SOCKET sock;
348
349                if (*pcbResponseInfoLen < sizeof(IPAddrEntry))
350                {
351                   return (STATUS_BUFFER_TOO_SMALL); 
352                }
353                
354                if (!WSCNTL_GetInterfaceName(pcommand->toi_entity.tei_instance, ifName))
355                {
356                   ERR ("Unable to parse /proc filesystem!\n");
357                   return (-1);
358                }
359                
360                
361                /* Get a socket so we can use ioctl */
362                if ( (sock = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
363                {
364                   ERR ("Error creating socket!\n");
365                   return (-1);
366                }
367                
368                /* 0 it out first */
369                memset(baseIPInfo, 0, sizeof(IPAddrEntry) );
370                
371                /* Interface Id */
372                baseIPInfo->iae_index     = pcommand->toi_entity.tei_instance;
373                
374                /* IP Address */
375                strcpy (ifInfo.ifr_name, ifName);
376                ifInfo.ifr_addr.sa_family = AF_INET;
377                if (ioctlsocket(sock, SIOCGIFADDR, (ULONG*)&ifInfo) < 0) 
378                {
379                   baseIPInfo->iae_addr = 0x0;
380                }
381                else
382                {
383                   struct ws_sockaddr_in *ipTemp = (struct ws_sockaddr_in *)&ifInfo.ifr_addr;
384                   baseIPInfo->iae_addr = ipTemp->sin_addr.S_un.S_addr;
385                }
386                
387                /* Broadcast Address */
388                strcpy (ifInfo.ifr_name, ifName);
389                if (ioctlsocket(sock, SIOCGIFBRDADDR, (ULONG *)&ifInfo) < 0)
390                {
391                   baseIPInfo->iae_bcastaddr = 0x0;
392                }
393                else
394                {
395                   struct ws_sockaddr_in *ipTemp = (struct ws_sockaddr_in *)&ifInfo.ifr_broadaddr;
396                   baseIPInfo->iae_bcastaddr = ipTemp->sin_addr.S_un.S_addr; 
397                }
398
399                /* Subnet Mask */
400                strcpy(ifInfo.ifr_name, ifName);
401                if (ioctlsocket(sock, SIOCGIFNETMASK, (ULONG *)&ifInfo) < 0)
402                {
403                   baseIPInfo->iae_mask = 0x0;
404                }
405                else
406                {
407                   /* Trying to avoid some compile problems across platforms.
408                      (Linux, FreeBSD, Solaris...) */
409                   #ifndef ifr_netmask
410                      #ifndef ifr_addr
411                         baseIPInfo->iae_mask = 0;
412                         ERR ("Unable to determine Netmask on your platform!\n");
413                      #else
414                         struct ws_sockaddr_in *ipTemp = (struct ws_sockaddr_in *)&ifInfo.ifr_addr;
415                         baseIPInfo->iae_mask = ipTemp->sin_addr.S_un.S_addr; 
416                      #endif
417                   #else  
418                      struct ws_sockaddr_in *ipTemp = (struct ws_sockaddr_in *)&ifInfo.ifr_netmask;
419                      baseIPInfo->iae_mask = ipTemp->sin_addr.S_un.S_addr; 
420                   #endif
421                }
422
423                /* FIXME: How should the below be properly calculated? ******************/
424                baseIPInfo->iae_reasmsize = 0x0;
425                baseIPInfo->iae_context   = 0x0;
426                baseIPInfo->iae_pad       = 0x0;
427                /************************************************************************/
428              
429                /* Calculate size of out buffer */
430                *pcbResponseInfoLen = sizeof(IPAddrEntry);
431                closesocket(sock);
432                break;
433             }
434
435
436             /* This call returns the routing table.
437              * No official documentation found, even the name of the command is unknown.
438              * Work is based on
439              * http://www.cyberport.com/~tangent/programming/winsock/articles/wscontrol.html
440              * and testings done with winipcfg.exe, route.exe and ipconfig.exe.
441              * pcommand->toi_entity.tei_instance seems to be the interface number
442              * but route.exe outputs only the information for the last interface
443              * if only the routes for the pcommand->toi_entity.tei_instance
444              * interface are returned. */
445             case IP_MIB_ROUTETABLE_ENTRY_ID:    /* FIXME: not real name. Value is 0x101 */
446             {
447                 int numRoutes, foundRoutes;
448                 wscntl_routeentry *routeTable, *routePtr;       /* route table */
449                 
450                 IPRouteEntry *winRouteTable  = (IPRouteEntry *) pResponseInfo;
451
452                 /* Get the number of routes */
453                 numRoutes = WSCNTL_GetEntryCount(WSCNTL_COUNT_ROUTES); 
454                 if (numRoutes < 0)
455                 {
456                     ERR ("Unable to open /proc filesystem to determine number of network routes!\n");
457                     return (-1); 
458                 }
459
460                 if (*pcbResponseInfoLen < (sizeof(IPRouteEntry) * numRoutes))
461                 {
462                     return (STATUS_BUFFER_TOO_SMALL); 
463                 }
464                 
465                 /* malloc space for the routeTable */
466                 routeTable = (wscntl_routeentry *) malloc(sizeof(wscntl_routeentry) * numRoutes);
467                 if (!routeTable)
468                 {
469                     ERR ("couldn't malloc space for routeTable!\n");
470                 }
471
472                 /* get the route table */
473                 foundRoutes = WSCNTL_GetRouteTable(numRoutes, routeTable);
474                 if (foundRoutes < 0)
475                 {
476                     ERR ("Unable to open /proc filesystem to parse the route entrys!\n");
477                     free(routeTable);
478                     return -1;
479                 }
480                 routePtr = routeTable;
481                     
482                 /* first 0 out the output buffer */
483                 memset(winRouteTable, 0, *pcbResponseInfoLen);
484                
485                 /* calculate the length of the data in the output buffer */
486                 *pcbResponseInfoLen = sizeof(IPRouteEntry) * foundRoutes;
487                 
488                 for ( ; foundRoutes > 0; foundRoutes--)
489                 {
490                     winRouteTable->ire_addr = routePtr->wre_dest;
491                     winRouteTable->ire_index = routePtr->wre_intf;
492                     winRouteTable->ire_metric = routePtr->wre_metric;
493                     /* winRouteTable->ire_option4 =
494                     winRouteTable->ire_option5 =
495                     winRouteTable->ire_option6 = */
496                     winRouteTable->ire_gw = routePtr->wre_gw;
497                     /* winRouteTable->ire_option8 =
498                     winRouteTable->ire_option9 =
499                     winRouteTable->ire_option10 = */
500                     winRouteTable->ire_mask = routePtr->wre_mask;
501                     /* winRouteTable->ire_option12 = */
502
503                     winRouteTable++;
504                     routePtr++;
505                 }
506                                                     
507                 free(routeTable);
508                 break;
509             }
510             
511
512             default: 
513             {
514                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",
515                        pcommand->toi_id, pcommand->toi_entity.tei_entity, pcommand->toi_entity.tei_instance, 
516                        pcommand->toi_class, pcommand->toi_type);
517               
518                return (WSAEOPNOTSUPP); 
519             }
520          }
521       
522          break;
523       }
524     
525       case WSCNTL_TCPIP_ICMP_ECHO:
526       {
527          unsigned int addr = *(unsigned int*)pRequestInfo;
528          #if 0
529             int timeout= *(unsigned int*)(inbuf+4);
530             short x1 = *(unsigned short*)(inbuf+8);
531             short sendbufsize = *(unsigned short*)(inbuf+10);
532             char x2 = *(unsigned char*)(inbuf+12);
533             char ttl = *(unsigned char*)(inbuf+13);
534             char service = *(unsigned char*)(inbuf+14);
535             char type= *(unsigned char*)(inbuf+15); /* 0x2: don't fragment*/
536          #endif      
537       
538          FIXME("(ICMP_ECHO) to 0x%08x stub \n", addr);
539          break;
540       }
541
542       default:
543       {
544          FIXME("Protocoll Not Supported -> protocoll=0x%lx, action=0x%lx, Request=%p, RequestLen=%p, Response=%p, ResponseLen=%p\n",
545                protocoll, action, pRequestInfo, pcbRequestInfoLen, pResponseInfo, pcbResponseInfoLen);
546       
547          return (WSAEOPNOTSUPP); 
548       }
549    }
550    
551    
552    return (WSCTL_SUCCESS); 
553 }
554
555
556
557 /* 
558   Helper function for WsControl - Get count of the number of interfaces
559   or routes by parsing /proc filesystem.
560 */
561 int WSCNTL_GetEntryCount(const int entrytype)
562 {
563    char *filename;
564    int  fd;
565    char buf[512];  /* Size optimized for a typical workstation */
566    char *ptr;
567    int  count;
568    int  chrread;
569  
570  
571    switch (entrytype)
572    {
573        case WSCNTL_COUNT_INTERFACES:
574        {
575            filename = PROCFS_NETDEV_FILE;
576            count = -2;  /* two haeder lines */
577            break;
578        };
579
580        case WSCNTL_COUNT_ROUTES:
581        {
582            filename = PROCFS_ROUTE_FILE;
583            count = -1;  /* one haeder line */
584            break;
585        };
586
587        default:
588        {
589            return -1;
590        };
591    }
592
593    /* open /proc filesystem file */
594    fd = open(filename, O_RDONLY);
595    if (fd < 0) {
596        return -1;
597    }
598
599    /* read the file and count the EOL's */
600    while ((chrread = read(fd, buf, sizeof(buf))) != 0) 
601    {
602        ptr = buf;
603        if (chrread < 0) 
604        {
605            if (errno == EINTR) 
606            {
607                continue;        /* read interupted by a signal, try to read again */
608            }
609            else
610            {
611                close(fd);
612                return -1;
613            }
614        }
615        while ((ptr = memchr(ptr, '\n', chrread - (int) (ptr -  buf))) > 0)
616        {
617            count++;
618            ptr++;
619        }
620    }
621
622    close(fd);
623    return count;
624 }
625
626
627 /*
628    Helper function for WsControl - Get name of device from interface number
629    by parsing /proc filesystem.
630 */
631 int WSCNTL_GetInterfaceName(int intNumber, char *intName)
632 {
633    FILE *procfs;
634    char buf[512]; /* Size doesn't matter, something big */
635    int  i;
636
637    /* Open /proc filesystem file for network devices */ 
638    procfs = fopen(PROCFS_NETDEV_FILE, "r");
639    if (!procfs) 
640    {
641       /* If we can't open the file, return an error */
642       return (-1);
643    }
644    
645    /* Omit first two lines, they are only headers */
646    fgets(buf, sizeof(buf), procfs);     
647    fgets(buf, sizeof(buf), procfs);
648
649    for (i=0; i<intNumber; i++)
650    {
651       /* Skip the lines that don't interest us. */
652       fgets(buf, sizeof(buf), procfs);
653    }
654    fgets(buf, sizeof(buf), procfs); /* This is the line we want */
655
656    
657    /* Parse out the line, grabbing only the name of the device
658       to the intName variable 
659       
660       The Line comes in like this: (we only care about the device name)
661       lo:   21970 377 0 0 0 0 0 0 21970 377 0 0 0 0 0 0
662    */
663    i=0; 
664    while (isspace(buf[i])) /* Skip initial space(s) */
665    {
666       i++;
667    }
668
669    while (buf[i]) 
670    {
671       if (isspace(buf[i]))
672       {
673          break;
674       }
675       
676       if (buf[i] == ':')  /* FIXME: Not sure if this block (alias detection) works properly */
677       {
678          /* This interface could be an alias... */
679          int hold = i;
680          char *dotname = intName;
681          *intName++ = buf[i++];
682          
683          while (isdigit(buf[i]))
684          {
685             *intName++ = buf[i++];
686          }
687          
688          if (buf[i] != ':') 
689          {
690             /* ... It wasn't, so back up */
691             i = hold;
692             intName = dotname;
693          }
694  
695          if (buf[i] == '\0')
696          {
697             fclose(procfs);
698             return(FALSE);
699          }
700          
701          i++;
702          break;
703       }
704       
705       *intName++ = buf[i++];
706    }
707    *intName++ = '\0';
708
709    fclose(procfs);
710    return(TRUE);
711 }
712
713
714 /*
715    Helper function for WsControl - This function returns the bytes (octets) transmitted
716    and received for the supplied interface number from the /proc fs. 
717 */
718 int WSCNTL_GetTransRecvStat(int intNumber, unsigned long *transBytes, unsigned long *recvBytes)
719 {
720    FILE *procfs;
721    char buf[512], result[512]; /* Size doesn't matter, something big */
722    int  i, bufPos, resultPos;
723
724    /* Open /proc filesystem file for network devices */ 
725    procfs = fopen(PROCFS_NETDEV_FILE, "r");
726    if (!procfs) 
727    {
728       /* If we can't open the file, return an error */
729       return (-1);
730    }
731    
732    /* Omit first two lines, they are only headers */
733    fgets(buf, sizeof(buf), procfs);     
734    fgets(buf, sizeof(buf), procfs);
735
736    for (i=0; i<intNumber; i++)
737    {
738       /* Skip the lines that don't interest us. */
739       fgets(buf, sizeof(buf), procfs);
740    }
741    fgets(buf, sizeof(buf), procfs); /* This is the line we want */
742
743
744
745    /* Parse out the line, grabbing the number of bytes transmitted
746       and received on the interface.
747       
748       The Line comes in like this: (we care about columns 2 and 10)
749       lo:   21970 377 0 0 0 0 0 0 21970 377 0 0 0 0 0 0
750    */
751
752    /* Start at character 0 in the buffer */
753    bufPos=0;
754    
755    /* Skip initial space(s) */ 
756    while (isspace(buf[bufPos])) 
757       bufPos++;
758
759
760    /* Skip the name and its trailing spaces (if any) */
761    while (buf[bufPos]) 
762    {
763       if (isspace(buf[bufPos]))
764          break;
765  
766       if (buf[bufPos] == ':') /* Could be an alias */
767       {
768          int hold = bufPos;
769
770          while(isdigit (buf[bufPos]))
771             bufPos++;
772          if (buf[bufPos] != ':')
773             bufPos = hold;
774          if (buf[bufPos] == '\0')
775          {
776             fclose(procfs);
777             return(FALSE);
778          }
779          
780          bufPos++;
781          break;
782       }
783
784       bufPos++;
785    }
786    while (isspace(buf[bufPos]))
787       bufPos++;
788
789
790    /* This column (#2) is the number of bytes received. */
791    resultPos = 0;
792    while (!isspace(buf[bufPos]))
793    {
794       result[resultPos] = buf[bufPos];
795       result[resultPos+1]='\0';
796       resultPos++; bufPos++;
797    }
798    *recvBytes = strtoul (result, NULL, 10); /* convert string to unsigned long, using base 10 */
799
800    
801    /* Skip columns #3 to #9 (Don't need them) */
802    for  (i=0; i<7; i++)
803    {
804       while (isspace(buf[bufPos]))
805          bufPos++;
806       while (!isspace(buf[bufPos])) 
807          bufPos++;
808    }
809
810
811    /* This column (#10) is the number of bytes transmitted */
812    while (isspace(buf[bufPos]))
813        bufPos++;
814
815    resultPos = 0;
816    while (!isspace(buf[bufPos]))
817    {
818       result[resultPos] = buf[bufPos];
819       result[resultPos+1]='\0';
820       resultPos++; bufPos++;
821    }
822    *transBytes = strtoul (result, NULL, 10); /* convert string to unsigned long, using base 10 */
823
824
825    fclose(procfs);
826    return(TRUE);
827 }
828
829
830 /* Parse the procfs route file and put the datas into routeTable.
831  * Return value is the number of found routes */
832 int WSCNTL_GetRouteTable(int numRoutes, wscntl_routeentry *routeTable)
833 {
834     int nrIntf;         /* total number of interfaces */
835     char buf[256];      /* temporary buffer */
836     char *ptr;          /* pointer to temporary buffer */
837     FILE *file;         /* file handle for procfs route file */
838     int foundRoutes = 0;        /* number of found routes */
839     typedef struct interface_t {
840         char intfName[IFNAMSIZ+1];      /* the name of the interface */
841         int intfNameLen;        /* length of interface name */
842     } interface_t;
843     interface_t *interface;
844     int intfNr;         /* the interface number */
845
846     wscntl_routeentry *routePtr = routeTable;
847
848     /* get the number of interfaces */
849     nrIntf = WSCNTL_GetEntryCount(WSCNTL_COUNT_INTERFACES);
850     if (nrIntf < 0)
851     {
852         ERR ("Unable to open /proc filesystem to determine number of network interfaces!\n");
853         return (-1);
854     }
855
856     /* malloc space for the interface struct array */
857     interface = (interface_t *) malloc(sizeof(interface_t) * nrIntf);
858     if (!routeTable)
859     {
860         ERR ("couldn't malloc space for interface!\n");
861     }
862
863     for (intfNr = 0; intfNr < nrIntf; intfNr++) {
864         if (WSCNTL_GetInterfaceName(intfNr, interface[intfNr].intfName) < 0)
865         {
866             ERR ("Unable to open /proc filesystem to determine the name of network interfaces!\n");
867             free(interface);
868             return (-1);
869         }
870         interface[intfNr].intfNameLen = strlen(interface[intfNr].intfName);
871     }
872
873     /* Open /proc filesystem file for routes */ 
874     file = fopen(PROCFS_ROUTE_FILE, "r");
875     if (!file) 
876     {
877         /* If we can't open the file, return an error */
878         free(interface);
879         return (-1);
880     }
881     
882     /* skip the header line */
883     fgets(buf, sizeof(buf), file);
884
885     /* parse the rest of the file and put the matching entrys into routeTable.
886        Format of procfs route entry:
887        Iface Destination Gateway Flags RefCnt Use Metric Mask  MTU Window IRTT
888        lo 0000007F 00000000 0001 0 0 0 000000FF 0 0 0
889     */
890     while (fgets(buf, sizeof(buf), file)) {
891         intfNr = 0;
892         /* find the interface of the route */
893         while ((strncmp(buf, interface[intfNr].intfName, interface[intfNr].intfNameLen) != 0) 
894                 && (intfNr < nrIntf))
895         {
896             intfNr++;
897         }
898         if (intfNr < nrIntf) {
899             foundRoutes++;
900             if (foundRoutes > numRoutes) {
901                 /* output buffer is to small */
902                 ERR("buffer to small to fit all routes found into it!\n");
903                 free(interface);
904                 fclose(file);
905                 return -1;
906             }
907             ptr = buf;
908             ptr += interface[intfNr].intfNameLen;
909             routePtr->wre_intf = intfNr;
910             routePtr->wre_dest = strtoul(ptr, &ptr, 16);        /* destination */
911             routePtr->wre_gw = strtoul(ptr, &ptr, 16);  /* gateway */
912             strtoul(ptr, &ptr, 16);     /* Flags; unused */
913             strtoul(ptr, &ptr, 16);     /* RefCnt; unused */
914             strtoul(ptr, &ptr, 16);     /* Use; unused */
915             routePtr->wre_metric = strtoul(ptr, &ptr, 16);      /* metric */
916             routePtr->wre_mask = strtoul(ptr, &ptr, 16);        /* mask */
917             /* strtoul(ptr, &ptr, 16);  MTU; unused */
918             /* strtoul(ptr, &ptr, 16);  Window; unused */
919             /* strtoul(ptr, &ptr, 16);  IRTT; unused */
920
921             routePtr++;
922         }
923         else
924         {
925             /* this should never happen */
926             WARN("Skipping route with unknown interface\n");
927         }
928     }
929
930     free(interface);
931     fclose(file);
932     return foundRoutes;
933 }
934
935
936 /***********************************************************************
937  *              WSARecvEx()                     (WSOCK32.1107)
938  *
939  * WSARecvEx is a Microsoft specific extension to winsock that is identical to recv
940  * except that has an in/out argument call flags that has the value MSG_PARTIAL ored
941  * into the flags parameter when a partial packet is read. This only applies to
942  * sockets using the datagram protocol. This method does not seem to be implemented
943  * correctly by microsoft as the winsock implementation does not set the MSG_PARTIAL
944  * flag when a fragmented packet arrives.
945  */
946 INT WINAPI WSARecvEx(SOCKET s, char *buf, INT len, INT *flags)
947 {
948     FIXME("(WSARecvEx) partial packet return value not set \n");
949     return recv(s, buf, len, *flags);
950 }
951
952
953 /***********************************************************************
954  *       WS_s_perror         (WSOCK32.1108)
955  */
956 void WINAPI WS_s_perror(LPCSTR message)
957 {
958     FIXME("(%s): stub\n",message);
959     return;
960 }