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