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