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