Adds an initial WSAIoctl function with support for only the
[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 #include "config.h"
8
9 #include <sys/types.h>
10 #include "windef.h"
11 #include "debugtools.h"
12 #include "winsock2.h"
13 #include "winnt.h"
14 #include "wscontrol.h"
15 #include <ctype.h>
16 #include <sys/ioctl.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20 #ifdef HAVE_NET_IF_H
21 # include <net/if.h>
22 #endif
23
24 DEFAULT_DEBUG_CHANNEL(winsock);
25
26
27 /***********************************************************************
28  *      WsControl()
29  *
30  * WsControl seems to be an undocumented Win95 function. A lot of 
31  * discussion about WsControl can be found on the net, e.g.
32  * Subject:      Re: WSOCK32.DLL WsControl Exported Function
33  * From:         "Peter Rindfuss" <rindfuss-s@medea.wz-berlin.de>
34  * Date:         1997/08/17
35  *
36  * WSCNTL_TCPIP_QUERY_INFO option is partially implemeted based
37  * on observing the behaviour of WsControl with an app in 
38  * Windows 98.  It is not fully implemented, and there could
39  * be (are?) errors due to incorrect assumptions made.
40  *
41  *
42  * WsControl returns WSCTL_SUCCESS on success.
43  * STATUS_BUFFER_TOO_SMALL is returned if the output buffer length
44  * (*pcbResponseInfoLen) is too small, otherwise errors return -1.
45  *
46  * It doesn't seem to generate errors that can be retrieved by 
47  * WSAGetLastError().
48  *
49  */
50
51 DWORD WINAPI WsControl(DWORD protocoll,
52                        DWORD action,
53                        LPVOID pRequestInfo,
54                        LPDWORD pcbRequestInfoLen,
55                        LPVOID pResponseInfo,
56                        LPDWORD pcbResponseInfoLen) 
57 {
58    /* Get the command structure into a pointer we can use,
59       rather than void */
60    TDIObjectID *pcommand = (TDIObjectID *)pRequestInfo;
61   
62    TRACE ("   WsControl TOI_ID=>0x%lx<, {TEI_ENTITY=0x%lx, TEI_INSTANCE=0x%lx}, TOI_CLASS=0x%lx, TOI_TYPE=0x%lx\n",
63           pcommand->toi_id, pcommand->toi_entity.tei_entity, pcommand->toi_entity.tei_instance,
64           pcommand->toi_class, pcommand->toi_type );
65   
66
67
68    switch (action) 
69    {
70       case WSCNTL_TCPIP_QUERY_INFO: 
71       {
72          switch (pcommand->toi_id)
73          {
74             /* 
75                ENTITY_LIST_ID seems to get number of adapters in the system.
76                (almost like an index to be used when calling other WsControl options)
77             */
78             case ENTITY_LIST_ID: 
79             {
80                TDIEntityID *baseptr = pResponseInfo;
81                int numInt = 0, i;
82
83                if (pcommand->toi_class != INFO_CLASS_GENERIC &&
84                    pcommand->toi_type != INFO_TYPE_PROVIDER) 
85                { 
86                   FIXME ("Unexpected Option for ENTITY_LIST_ID request -> toi_class=0x%lx, toi_type=0x%lx\n",
87                        pcommand->toi_class, pcommand->toi_type);
88                   return (WSAEOPNOTSUPP); 
89                }
90            
91                numInt = WSCNTL_GetInterfaceCount(); 
92                if (numInt < 0)
93                {
94                   ERR ("Unable to open /proc filesystem to determine number of network interfaces!\n");
95                   return (-1); 
96                }
97
98                if (*pcbResponseInfoLen < sizeof(TDIEntityID)*(numInt*2) ) 
99                {
100                   return (STATUS_BUFFER_TOO_SMALL);
101                }
102            
103                /* 0 it out first */
104                memset(baseptr, 0, sizeof(TDIEntityID)*(numInt*2)); 
105                
106                for (i=0; i<numInt; i++)
107                {
108                   /* tei_instance is an network interface identifier.
109                      I'm not quite sure what the difference is between tei_entity values of 
110                      CL_NL_ENTITY and IF_ENTITY */
111                   baseptr->tei_entity = CL_NL_ENTITY;  baseptr->tei_instance = i; baseptr++;
112                   baseptr->tei_entity = IF_ENTITY;     baseptr->tei_instance = i; baseptr++; 
113                }
114
115                /* Calculate size of out buffer */
116                *pcbResponseInfoLen = sizeof(TDIEntityID)*(numInt*2);
117             
118                break;
119             }
120           
121           
122             /* ENTITY_TYPE_ID is used to obtain simple information about a 
123                network card, such as MAC Address, description, interface type,
124                number of network addresses, etc. */
125             case ENTITY_TYPE_ID:  /* ALSO: IP_MIB_STATS_ID */
126             {
127                if (pcommand->toi_class == INFO_CLASS_GENERIC && pcommand->toi_type == INFO_TYPE_PROVIDER) 
128                {
129                   if (pcommand->toi_entity.tei_entity == IF_ENTITY)
130                   {
131                      * ((ULONG *)pResponseInfo) = IF_MIB; 
132
133                      /* Calculate size of out buffer */
134                      *pcbResponseInfoLen = sizeof (ULONG);
135
136                   }
137                   else if (pcommand->toi_entity.tei_entity == CL_NL_ENTITY) 
138                   {
139                      * ((ULONG *)pResponseInfo) = CL_NL_IP;  
140
141                      /* Calculate size of out buffer */
142                      *pcbResponseInfoLen = sizeof (ULONG); 
143                   }
144                }
145                else if (pcommand->toi_class == INFO_CLASS_PROTOCOL &&
146                         pcommand->toi_type == INFO_TYPE_PROVIDER)
147                {
148                   if (pcommand->toi_entity.tei_entity == IF_ENTITY)
149                   {
150                      /* In this case, we are requesting specific information about a 
151                         a particular network adapter. (MAC Address, speed, data transmitted/received,
152                         etc.)
153                      */ 
154                      IFEntry *IntInfo = (IFEntry *) pResponseInfo;
155                      char ifName[512];
156                      struct ifreq ifInfo;
157                      int sock;
158
159                      
160                      if (!WSCNTL_GetInterfaceName(pcommand->toi_entity.tei_instance, ifName))
161                      {
162                         ERR ("Unable to parse /proc filesystem!\n");
163                         return (-1);
164                      }
165                
166                      /* Get a socket so that we can use ioctl */
167                      if ( (sock = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
168                      {
169                         ERR ("Error creating socket!\n");
170                         return (-1);
171                      }
172                    
173                      /* 0 out return structure first */
174                      memset (IntInfo, 0, sizeof(IFEntry));
175                      
176                      /* Interface ID */
177                      IntInfo->if_index = pcommand->toi_entity.tei_instance;
178                      
179                      /* MAC Address */
180                      strcpy(ifInfo.ifr_name, ifName);
181                      if (ioctl(sock, SIOCGIFHWADDR, &ifInfo) < 0)
182                      {
183                         ERR ("Error obtaining MAC Address!\n");
184                         close(sock);
185                         return (-1);
186                      }
187                      else
188                      {
189                         /* FIXME: Is it correct to assume size of 6? */
190                         memcpy(IntInfo->if_physaddr, ifInfo.ifr_hwaddr.sa_data, 6);
191                         IntInfo->if_physaddrlen=6;
192                      }
193                      
194                      /* Interface name and length */
195                      strcpy (IntInfo->if_descr, ifName);
196                      IntInfo->if_descrlen= strlen (IntInfo->if_descr);
197                      
198                      /* Obtain bytes transmitted/received for interface */
199                      if ( (WSCNTL_GetTransRecvStat(pcommand->toi_entity.tei_instance, 
200                            &IntInfo->if_inoctets, &IntInfo->if_outoctets)) < 0)
201                      {
202                         ERR ("Error obtaining transmit/receive stats for the network interface!\n");
203                         close(sock);
204                         return (-1);
205                      }
206                      
207                      
208                      /* FIXME: How should the below be properly calculated? ******************/
209                      IntInfo->if_type =  0x6; /* Ethernet (?) */
210                      IntInfo->if_speed = 1000000; /* Speed of interface (bits per second?) */
211                      /************************************************************************/
212
213                      close(sock);
214                      *pcbResponseInfoLen = sizeof (IFEntry) + IntInfo->if_descrlen; 
215                   }
216                   else if (pcommand->toi_entity.tei_entity == CL_NL_ENTITY) 
217                   {
218                      IPSNMPInfo *infoStruc = (IPSNMPInfo *) pResponseInfo;
219                      int numInt;
220                      
221                      /* This case is used to obtain general statistics about the 
222                         network */
223                      
224                      if (*pcbResponseInfoLen < sizeof(IPSNMPInfo) )
225                      {
226                         return (STATUS_BUFFER_TOO_SMALL);
227                      }
228                      else
229                      {
230                         /* 0 it out first */
231                         memset(infoStruc, 0, sizeof(IPSNMPInfo));
232             
233                         /* Get the number of interfaces */
234                         numInt = WSCNTL_GetInterfaceCount(); 
235                         if (numInt < 0)
236                         {
237                            ERR ("Unable to open /proc filesystem to determine number of network interfaces!\n");
238                            return (-1); 
239                         }
240
241                         infoStruc->ipsi_numif           = numInt; /* # of interfaces */
242                         infoStruc->ipsi_numaddr         = numInt; /* # of addresses */
243                         infoStruc->ipsi_numroutes       = numInt; /* # of routes ~ FIXME - Is this right? */
244
245                         /* FIXME: How should the below be properly calculated? ******************/
246                         infoStruc->ipsi_forwarding      = 0x0;
247                         infoStruc->ipsi_defaultttl      = 0x0;
248                         infoStruc->ipsi_inreceives      = 0x0;
249                         infoStruc->ipsi_inhdrerrors     = 0x0;
250                         infoStruc->ipsi_inaddrerrors    = 0x0;
251                         infoStruc->ipsi_forwdatagrams   = 0x0;
252                         infoStruc->ipsi_inunknownprotos = 0x0;
253                         infoStruc->ipsi_indiscards      = 0x0;
254                         infoStruc->ipsi_indelivers      = 0x0;
255                         infoStruc->ipsi_outrequests     = 0x0;
256                         infoStruc->ipsi_routingdiscards = 0x0;
257                         infoStruc->ipsi_outdiscards     = 0x0;
258                         infoStruc->ipsi_outnoroutes     = 0x0;
259                         infoStruc->ipsi_reasmtimeout    = 0x0;
260                         infoStruc->ipsi_reasmreqds      = 0x0;
261                         infoStruc->ipsi_reasmoks        = 0x0;
262                         infoStruc->ipsi_reasmfails      = 0x0;
263                         infoStruc->ipsi_fragoks         = 0x0;
264                         infoStruc->ipsi_fragfails       = 0x0;
265                         infoStruc->ipsi_fragcreates     = 0x0;
266                         /************************************************************************/
267                       
268                         /* Calculate size of out buffer */
269                         *pcbResponseInfoLen = sizeof(IPSNMPInfo);
270                      }
271                   }
272                }
273                else
274                {
275                   FIXME ("Unexpected Option for ENTITY_TYPE_ID request -> toi_class=0x%lx, toi_type=0x%lx\n",
276                        pcommand->toi_class, pcommand->toi_type);
277                   
278                   return (WSAEOPNOTSUPP); 
279                }
280
281                break;
282             }
283
284
285             /* IP_MIB_ADDRTABLE_ENTRY_ID is used to obtain more detailed information about a 
286                particular network adapter */
287             case IP_MIB_ADDRTABLE_ENTRY_ID: 
288             {
289                IPAddrEntry *baseIPInfo = (IPAddrEntry *) pResponseInfo;
290                char ifName[512];
291                struct ifreq ifInfo;
292                int sock;
293
294                if (*pcbResponseInfoLen < sizeof(IPAddrEntry))
295                {
296                   return (STATUS_BUFFER_TOO_SMALL); 
297                }
298                
299                if (!WSCNTL_GetInterfaceName(pcommand->toi_entity.tei_instance, ifName))
300                {
301                   ERR ("Unable to parse /proc filesystem!\n");
302                   return (-1);
303                }
304                
305                
306                /* Get a socket so we can use ioctl */
307                if ( (sock = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
308                {
309                   ERR ("Error creating socket!\n");
310                   return (-1);
311                }
312                
313                /* 0 it out first */
314                memset(baseIPInfo, 0, sizeof(IPAddrEntry) );
315                
316                /* Interface Id */
317                baseIPInfo->iae_index     = pcommand->toi_entity.tei_instance;
318                
319                /* IP Address */
320                strcpy (ifInfo.ifr_name, ifName);
321                ifInfo.ifr_addr.sa_family = AF_INET;
322                if (ioctl(sock, SIOCGIFADDR, &ifInfo) < 0) 
323                {
324                   baseIPInfo->iae_addr = 0x0;
325                }
326                else
327                {
328                   struct ws_sockaddr_in *ipTemp = (struct ws_sockaddr_in *)&ifInfo.ifr_addr;
329                   baseIPInfo->iae_addr = ipTemp->sin_addr.S_un.S_addr;
330                }
331                
332                /* Broadcast Address */
333                strcpy (ifInfo.ifr_name, ifName);
334                if (ioctl(sock, SIOCGIFBRDADDR, &ifInfo) < 0)
335                {
336                   baseIPInfo->iae_bcastaddr = 0x0;
337                }
338                else
339                {
340                   struct ws_sockaddr_in *ipTemp = (struct ws_sockaddr_in *)&ifInfo.ifr_broadaddr;
341                   baseIPInfo->iae_bcastaddr = ipTemp->sin_addr.S_un.S_addr; 
342                }
343
344                /* Subnet Mask */
345                strcpy(ifInfo.ifr_name, ifName);
346                if (ioctl(sock, SIOCGIFNETMASK, &ifInfo) < 0)
347                {
348                   baseIPInfo->iae_mask = 0x0;
349                }
350                else
351                {
352                   struct ws_sockaddr_in *ipTemp = (struct ws_sockaddr_in *)&ifInfo.ifr_netmask;
353                   baseIPInfo->iae_mask = ipTemp->sin_addr.S_un.S_addr; 
354                }
355
356                /* FIXME: How should the below be properly calculated? ******************/
357                baseIPInfo->iae_reasmsize = 0x0;
358                baseIPInfo->iae_context   = 0x0;
359                baseIPInfo->iae_pad       = 0x0;
360                /************************************************************************/
361              
362                /* Calculate size of out buffer */
363                *pcbResponseInfoLen = sizeof(IPAddrEntry);
364                close(sock);
365                break;
366             }
367
368
369             default: 
370             {
371                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",
372                        pcommand->toi_id, pcommand->toi_entity.tei_entity, pcommand->toi_entity.tei_instance, 
373                        pcommand->toi_class, pcommand->toi_type);
374               
375                return (WSAEOPNOTSUPP); 
376             }
377          }
378       
379          break;
380       }
381     
382       case WSCNTL_TCPIP_ICMP_ECHO:
383       {
384          unsigned int addr = *(unsigned int*)pRequestInfo;
385          #if 0
386             int timeout= *(unsigned int*)(inbuf+4);
387             short x1 = *(unsigned short*)(inbuf+8);
388             short sendbufsize = *(unsigned short*)(inbuf+10);
389             char x2 = *(unsigned char*)(inbuf+12);
390             char ttl = *(unsigned char*)(inbuf+13);
391             char service = *(unsigned char*)(inbuf+14);
392             char type= *(unsigned char*)(inbuf+15); /* 0x2: don't fragment*/
393          #endif      
394       
395          FIXME("(ICMP_ECHO) to 0x%08x stub \n", addr);
396          break;
397       }
398
399       default:
400       {
401          FIXME("Protocoll Not Supported -> protocoll=0x%lx, action=0x%lx, Request=%p, RequestLen=%p, Response=%p, ResponseLen=%p\n",
402                protocoll, action, pRequestInfo, pcbRequestInfoLen, pResponseInfo, pcbResponseInfoLen);
403       
404          return (WSAEOPNOTSUPP); 
405       }
406    }
407    
408    
409    return (WSCTL_SUCCESS); 
410 }
411
412
413
414 /* 
415   Helper function for WsControl - Get count of the number of interfaces
416   by parsing /proc filesystem.
417 */
418 int WSCNTL_GetInterfaceCount(void)
419 {
420    FILE *procfs;
421    char buf[512];  /* Size doesn't matter, something big */
422    int  intcnt=0;
423  
424  
425    /* Open /proc filesystem file for network devices */ 
426    procfs = fopen(PROCFS_NETDEV_FILE, "r");
427    if (!procfs) 
428    {
429       /* If we can't open the file, return an error */
430       return (-1);
431    }
432    
433    /* Omit first two lines, they are only headers */
434    fgets(buf, sizeof buf, procfs);      
435    fgets(buf, sizeof buf, procfs);
436
437    while (fgets(buf, sizeof buf, procfs)) 
438    {
439       /* Each line in the file represents a network interface */
440       intcnt++;
441    }
442
443    fclose(procfs);
444    return(intcnt);
445 }
446
447
448 /*
449    Helper function for WsControl - Get name of device from interface number
450    by parsing /proc filesystem.
451 */
452 int WSCNTL_GetInterfaceName(int intNumber, char *intName)
453 {
454    FILE *procfs;
455    char buf[512]; /* Size doesn't matter, something big */
456    int  i;
457
458    /* Open /proc filesystem file for network devices */ 
459    procfs = fopen(PROCFS_NETDEV_FILE, "r");
460    if (!procfs) 
461    {
462       /* If we can't open the file, return an error */
463       return (-1);
464    }
465    
466    /* Omit first two lines, they are only headers */
467    fgets(buf, sizeof(buf), procfs);     
468    fgets(buf, sizeof(buf), procfs);
469
470    for (i=0; i<intNumber; i++)
471    {
472       /* Skip the lines that don't interest us. */
473       fgets(buf, sizeof(buf), procfs);
474    }
475    fgets(buf, sizeof(buf), procfs); /* This is the line we want */
476
477    
478    /* Parse out the line, grabbing only the name of the device
479       to the intName variable 
480       
481       The Line comes in like this: (we only care about the device name)
482       lo:   21970 377 0 0 0 0 0 0 21970 377 0 0 0 0 0 0
483    */
484    i=0; 
485    while (isspace(buf[i])) /* Skip initial space(s) */
486    {
487       i++;
488    }
489
490    while (buf[i]) 
491    {
492       if (isspace(buf[i]))
493       {
494          break;
495       }
496       
497       if (buf[i] == ':')  /* FIXME: Not sure if this block (alias detection) works properly */
498       {
499          /* This interface could be an alias... */
500          int hold = i;
501          char *dotname = intName;
502          *intName++ = buf[i++];
503          
504          while (isdigit(buf[i]))
505          {
506             *intName++ = buf[i++];
507          }
508          
509          if (buf[i] != ':') 
510          {
511             /* ... It wasn't, so back up */
512             i = hold;
513             intName = dotname;
514          }
515  
516          if (buf[i] == '\0')
517          {
518             fclose(procfs);
519             return(FALSE);
520          }
521          
522          i++;
523          break;
524       }
525       
526       *intName++ = buf[i++];
527    }
528    *intName++ = '\0';
529
530    fclose(procfs);
531    return(TRUE);
532 }
533
534
535 /*
536    Helper function for WsControl - This function returns the bytes (octets) transmitted
537    and received for the supplied interface number from the /proc fs. 
538 */
539 int WSCNTL_GetTransRecvStat(int intNumber, ulong *transBytes, ulong *recvBytes)
540 {
541    FILE *procfs;
542    char buf[512], result[512]; /* Size doesn't matter, something big */
543    int  i, bufPos, resultPos;
544
545    /* Open /proc filesystem file for network devices */ 
546    procfs = fopen(PROCFS_NETDEV_FILE, "r");
547    if (!procfs) 
548    {
549       /* If we can't open the file, return an error */
550       return (-1);
551    }
552    
553    /* Omit first two lines, they are only headers */
554    fgets(buf, sizeof(buf), procfs);     
555    fgets(buf, sizeof(buf), procfs);
556
557    for (i=0; i<intNumber; i++)
558    {
559       /* Skip the lines that don't interest us. */
560       fgets(buf, sizeof(buf), procfs);
561    }
562    fgets(buf, sizeof(buf), procfs); /* This is the line we want */
563
564
565
566    /* Parse out the line, grabbing the number of bytes transmitted
567       and received on the interface.
568       
569       The Line comes in like this: (we care about columns 2 and 10)
570       lo:   21970 377 0 0 0 0 0 0 21970 377 0 0 0 0 0 0
571    */
572
573    /* Start at character 0 in the buffer */
574    bufPos=0;
575    
576    /* Skip initial space(s) */ 
577    while (isspace(buf[bufPos])) 
578       bufPos++;
579
580
581    /* Skip the name and its trailing spaces (if any) */
582    while (buf[bufPos]) 
583    {
584       if (isspace(buf[bufPos]))
585          break;
586  
587       if (buf[bufPos] == ':') /* Could be an alias */
588       {
589          int hold = bufPos;
590
591          while(isdigit (buf[bufPos]))
592             bufPos++;
593          if (buf[bufPos] != ':')
594             bufPos = hold;
595          if (buf[bufPos] == '\0')
596          {
597             fclose(procfs);
598             return(FALSE);
599          }
600          
601          bufPos++;
602          break;
603       }
604
605       bufPos++;
606    }
607    while (isspace(buf[bufPos]))
608       bufPos++;
609
610
611    /* This column (#2) is the number of bytes received. */
612    resultPos = 0;
613    while (!isspace(buf[bufPos]))
614    {
615       result[resultPos] = buf[bufPos];
616       result[resultPos+1]='\0';
617       resultPos++; bufPos++;
618    }
619    *recvBytes = strtoul (result, NULL, 10); /* convert string to ulong, using base 10 */
620
621    
622    /* Skip columns #3 to #9 (Don't need them) */
623    for  (i=0; i<7; i++)
624    {
625       while (isspace(buf[bufPos]))
626          bufPos++;
627       while (!isspace(buf[bufPos])) 
628          bufPos++;
629    }
630
631
632    /* This column (#10) is the number of bytes transmitted */
633    while (isspace(buf[bufPos]))
634        bufPos++;
635
636    resultPos = 0;
637    while (!isspace(buf[bufPos]))
638    {
639       result[resultPos] = buf[bufPos];
640       result[resultPos+1]='\0';
641       resultPos++; bufPos++;
642    }
643    *transBytes = strtoul (result, NULL, 10); /* convert string to ulong, using base 10 */
644
645
646    fclose(procfs);
647    return(TRUE);
648 }
649
650
651
652
653 /***********************************************************************
654  *       WS_s_perror         (WSOCK32.1108)
655  */
656 void WINAPI WS_s_perror(LPCSTR message)
657 {
658     FIXME("(%s): stub\n",message);
659     return;
660 }