Avoid excessive heap memory reallocation when generating EMF
[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  * Copyright (C) 2003 Juan Lang.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22
23 #include "config.h"
24 #include <stdarg.h>
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "wine/debug.h"
29 #include "winsock2.h"
30 #include "winnt.h"
31 #include "wscontrol.h"
32 #include "iphlpapi.h"
33
34 WINE_DEFAULT_DEBUG_CHANNEL(winsock);
35
36 /* internal remapper function for the IP_ constants */
37 static INT _remap_optname(INT level, INT optname)
38 {
39   TRACE("level=%d, optname=%d\n", level, optname);
40   if (level == IPPROTO_IP) {
41     switch (optname) {       /***** from value *****/
42       case 2: return 9;      /* IP_MULTICAST_IF    */
43       case 3: return 10;     /* IP_MULTICAST_TTL   */
44       case 4: return 11;     /* IP_MULTICAST_LOOP  */
45       case 5: return 12;     /* IP_ADD_MEMBERSHIP  */
46       case 6: return 13;     /* IP_DROP_MEMBERSHIP */
47       case 7: return 4;      /* IP_TTL             */
48       case 8: return 3;      /* IP_TOS             */
49       case 9: return 14;     /* IP_DONTFRAGMENT    */
50       default: FIXME("Unknown optname %d, can't remap!\n", optname); return optname; 
51     }
52   } else {
53     /* don't need to do anything */
54     return optname;
55   }
56 }
57
58 /***********************************************************************
59  *              setsockopt              (WSOCK32.21)
60  *
61  * We have these forwarders because, for reasons unknown to us mere mortals,
62  * the values of the IP_ constants changed between winsock.h and winsock2.h.
63  * So, we need to remap them here.
64  */
65 INT WINAPI WS1_setsockopt(SOCKET s, INT level, INT optname, char *optval, INT optlen)
66 {
67   return setsockopt(s, level, _remap_optname(level, optname), optval, optlen);
68 }
69
70 /***********************************************************************
71  *              getsockopt              (WSOCK32.7)
72  */
73 INT WINAPI WS1_getsockopt(SOCKET s, INT level, INT optname, char *optval, INT *optlen)
74 {
75   return getsockopt(s, level, _remap_optname(level, optname), optval, optlen);
76 }
77
78 /***********************************************************************
79  *              WsControl (WSOCK32.1001)
80  *
81  * WsControl seems to be an undocumented Win95 function. A lot of
82  * discussion about WsControl can be found on the net, e.g.
83  * Subject:      Re: WSOCK32.DLL WsControl Exported Function
84  * From:         "Peter Rindfuss" <rindfuss-s@medea.wz-berlin.de>
85  * Date:         1997/08/17
86  *
87  * The WSCNTL_TCPIP_QUERY_INFO option is partially implemented based
88  * on observing the behaviour of WsControl with an app in
89  * Windows 98.  It is not fully implemented, and there could
90  * be (are?) errors due to incorrect assumptions made.
91  *
92  *
93  * WsControl returns WSCTL_SUCCESS on success.
94  * ERROR_LOCK_VIOLATION is returned if the output buffer length
95  * (*pcbResponseInfoLen) is too small.  This is an unusual error code, but
96  * it matches Win98's behavior.  Other errors come from winerror.h, not from
97  * winsock.h.  Again, this is to match Win98 behavior.
98  *
99  */
100
101 DWORD WINAPI WsControl(DWORD protocol,
102                        DWORD action,
103                        LPVOID pRequestInfo,
104                        LPDWORD pcbRequestInfoLen,
105                        LPVOID pResponseInfo,
106                        LPDWORD pcbResponseInfoLen)
107 {
108
109    /* Get the command structure into a pointer we can use,
110       rather than void */
111    TDIObjectID *pcommand = (TDIObjectID *)pRequestInfo;
112
113    /* validate input parameters.  Error codes are from winerror.h, not from
114     * winsock.h.  pcbResponseInfoLen is apparently allowed to be NULL for some
115     * commands, since winipcfg.exe fails if we ensure it's non-NULL in every
116     * case.
117     */
118    if (protocol != IPPROTO_TCP) return ERROR_INVALID_PARAMETER;
119    if (!pcommand) return ERROR_INVALID_PARAMETER;
120    if (!pcbRequestInfoLen) return ERROR_INVALID_ACCESS;
121    if (*pcbRequestInfoLen < sizeof(TDIObjectID)) return ERROR_INVALID_ACCESS;
122    if (!pResponseInfo) return ERROR_INVALID_PARAMETER;
123    if (pcommand->toi_type != INFO_TYPE_PROVIDER) return ERROR_INVALID_PARAMETER;
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,
127       pcommand->toi_entity.tei_instance,
128       pcommand->toi_class, pcommand->toi_type );
129
130    switch (action)
131    {
132    case WSCNTL_TCPIP_QUERY_INFO:
133    {
134       if (pcommand->toi_class != INFO_CLASS_GENERIC &&
135        pcommand->toi_class != INFO_CLASS_PROTOCOL)
136       {
137          ERR("Unexpected class %ld for WSCNTL_TCPIP_QUERY_INFO",
138           pcommand->toi_class);
139          return ERROR_BAD_ENVIRONMENT;
140       }
141
142       switch (pcommand->toi_id)
143       {
144          /* ENTITY_LIST_ID gets the list of "entity IDs", where an entity
145             may represent an interface, or a datagram service, or address
146             translation, or other fun things.  Typically an entity ID represents
147             a class of service, which is further queried for what type it is.
148             Different types will then have more specific queries defined.
149          */
150          case ENTITY_LIST_ID:
151          {
152             TDIEntityID *baseptr = (TDIEntityID *)pResponseInfo;
153             DWORD numInt, i, ifTable, spaceNeeded;
154             PMIB_IFTABLE table;
155
156             if (!pcbResponseInfoLen)
157                return ERROR_BAD_ENVIRONMENT;
158             if (pcommand->toi_class != INFO_CLASS_GENERIC)
159             {
160                FIXME ("Unexpected Option for ENTITY_LIST_ID request -> toi_class=0x%lx",
161                     pcommand->toi_class);
162                return (ERROR_BAD_ENVIRONMENT);
163             }
164
165             GetNumberOfInterfaces(&numInt);
166             spaceNeeded = sizeof(TDIEntityID) * (numInt * 2 + 3);
167
168             if (*pcbResponseInfoLen < spaceNeeded)
169                return (ERROR_LOCK_VIOLATION);
170
171             ifTable = 0;
172             GetIfTable(NULL, &ifTable, FALSE);
173             table = (PMIB_IFTABLE)calloc(1, ifTable);
174             if (!table)
175                return ERROR_NOT_ENOUGH_MEMORY;
176             GetIfTable(table, &ifTable, FALSE);
177
178             spaceNeeded = sizeof(TDIEntityID) * (table->dwNumEntries + 4);
179             if (*pcbResponseInfoLen < spaceNeeded)
180             {
181                free(table);
182                return (ERROR_LOCK_VIOLATION);
183             }
184
185             memset(baseptr, 0, spaceNeeded);
186
187             for (i = 0; i < table->dwNumEntries; i++)
188             {
189                /* Return IF_GENERIC and CL_NL_ENTITY on every interface, and
190                 * AT_ENTITY, CL_TL_ENTITY, and CO_TL_ENTITY on the first
191                 * interface.  MS returns them only on the loopback interface,
192                 * but it doesn't seem to matter.
193                 */
194                if (i == 0)
195                {
196                   baseptr->tei_entity = CO_TL_ENTITY;
197                   baseptr->tei_instance = table->table[i].dwIndex;
198                   baseptr++;
199                   baseptr->tei_entity = CL_TL_ENTITY;
200                   baseptr->tei_instance = table->table[i].dwIndex;
201                   baseptr++;
202                   baseptr->tei_entity = AT_ENTITY;
203                   baseptr->tei_instance = table->table[i].dwIndex;
204                   baseptr++;
205                }
206                baseptr->tei_entity = CL_NL_ENTITY;
207                baseptr->tei_instance = table->table[i].dwIndex;
208                baseptr++;
209                baseptr->tei_entity = IF_GENERIC;
210                baseptr->tei_instance = table->table[i].dwIndex;
211                baseptr++;
212             }
213
214             *pcbResponseInfoLen = spaceNeeded;
215             free(table);
216
217             break;
218          }
219
220          /* Returns MIB-II statistics for an interface */
221          case ENTITY_TYPE_ID:
222             switch (pcommand->toi_entity.tei_entity)
223             {
224             case IF_GENERIC:
225                if (pcommand->toi_class == INFO_CLASS_GENERIC)
226                {
227                   if (!pcbResponseInfoLen)
228                      return ERROR_BAD_ENVIRONMENT;
229                   *((ULONG *)pResponseInfo) = IF_MIB;
230                   *pcbResponseInfoLen = sizeof(ULONG);
231                }
232                else if (pcommand->toi_class == INFO_CLASS_PROTOCOL)
233                {
234                   MIB_IFROW row;
235                   DWORD index = pcommand->toi_entity.tei_instance, ret;
236                   DWORD size = sizeof(row) - sizeof(row.wszName) -
237                    sizeof(row.bDescr);
238
239                   if (!pcbResponseInfoLen)
240                      return ERROR_BAD_ENVIRONMENT;
241                   if (*pcbResponseInfoLen < size)
242                      return (ERROR_LOCK_VIOLATION);
243                   row.dwIndex = index;
244                   ret = GetIfEntry(&row);
245                   if (ret != NO_ERROR)
246                   {
247                      /* FIXME: Win98's arp.exe insists on querying index 1 for
248                       * its MIB-II stats, regardless of the tei_instances
249                       * returned in the ENTITY_LIST query above.  If the query
250                       * fails, arp.exe fails.  So, I do this hack return value
251                       * if index is 1 and the query failed just to get arp.exe
252                       * to continue.
253                       */
254                      if (index == 1)
255                         return NO_ERROR;
256                      ERR ("Error retrieving data for interface index %lu\n",
257                       index);
258                      return ret;
259                   }
260                   size = sizeof(row) - sizeof(row.wszName) -
261                    sizeof(row.bDescr) + row.dwDescrLen;
262                   if (*pcbResponseInfoLen < size)
263                      return (ERROR_LOCK_VIOLATION);
264                   memcpy(pResponseInfo, &row.dwIndex, size);
265                   *pcbResponseInfoLen = size;
266                }
267                break;
268
269             /* Returns address-translation related data.  In our case, this is
270              * ARP.
271              */
272             case AT_ENTITY:
273                if (pcommand->toi_class == INFO_CLASS_GENERIC)
274                {
275                   if (!pcbResponseInfoLen)
276                      return ERROR_BAD_ENVIRONMENT;
277                   *((ULONG *)pResponseInfo) = AT_ARP;
278                   *pcbResponseInfoLen = sizeof(ULONG);
279                }
280                else if (pcommand->toi_class == INFO_CLASS_PROTOCOL)
281                {
282                   PMIB_IPNETTABLE table;
283                   DWORD size;
284                   PULONG output = (PULONG)pResponseInfo;
285
286                   if (!pcbResponseInfoLen)
287                      return ERROR_BAD_ENVIRONMENT;
288                   if (*pcbResponseInfoLen < sizeof(ULONG) * 2)
289                      return (ERROR_LOCK_VIOLATION);
290                   GetIpNetTable(NULL, &size, FALSE);
291                   table = (PMIB_IPNETTABLE)calloc(1, size);
292                   if (!table)
293                      return ERROR_NOT_ENOUGH_MEMORY;
294                   GetIpNetTable(table, &size, FALSE);
295                   /* FIXME: I don't understand the meaning of the ARP output
296                    * very well, but it seems to indicate how many ARP entries
297                    * exist.  I don't know whether this should reflect the
298                    * number per interface, as I'm only testing with a single
299                    * interface.  So, I lie and say all ARP entries exist on
300                    * a single interface--the first one that appears in the
301                    * ARP table.
302                    */
303                   *(output++) = table->dwNumEntries;
304                   *output = table->table[0].dwIndex;
305                   free(table);
306                   *pcbResponseInfoLen = sizeof(ULONG) * 2;
307                }
308                break;
309
310             /* Returns connectionless network layer statistics--in our case,
311              * this is IP.
312              */
313             case CL_NL_ENTITY:
314                if (pcommand->toi_class == INFO_CLASS_GENERIC)
315                {
316                   if (!pcbResponseInfoLen)
317                      return ERROR_BAD_ENVIRONMENT;
318                   *((ULONG *)pResponseInfo) = CL_NL_IP;
319                   *pcbResponseInfoLen = sizeof(ULONG);
320                }
321                else if (pcommand->toi_class == INFO_CLASS_PROTOCOL)
322                {
323                   if (!pcbResponseInfoLen)
324                      return ERROR_BAD_ENVIRONMENT;
325                   if (*pcbResponseInfoLen < sizeof(MIB_IPSTATS))
326                      return ERROR_LOCK_VIOLATION;
327                   GetIpStatistics((PMIB_IPSTATS)pResponseInfo);
328
329                   *pcbResponseInfoLen = sizeof(MIB_IPSTATS);
330                }
331                break;
332
333             /* Returns connectionless transport layer statistics--in our case,
334              * this is UDP.
335              */
336             case CL_TL_ENTITY:
337                if (pcommand->toi_class == INFO_CLASS_GENERIC)
338                {
339                   if (!pcbResponseInfoLen)
340                      return ERROR_BAD_ENVIRONMENT;
341                   *((ULONG *)pResponseInfo) = CL_TL_UDP;
342                   *pcbResponseInfoLen = sizeof(ULONG);
343                }
344                else if (pcommand->toi_class == INFO_CLASS_PROTOCOL)
345                {
346                   if (!pcbResponseInfoLen)
347                      return ERROR_BAD_ENVIRONMENT;
348                   if (*pcbResponseInfoLen < sizeof(MIB_UDPSTATS))
349                      return ERROR_LOCK_VIOLATION;
350                   GetUdpStatistics((PMIB_UDPSTATS)pResponseInfo);
351                   *pcbResponseInfoLen = sizeof(MIB_UDPSTATS);
352                }
353                break;
354
355             /* Returns connection-oriented transport layer statistics--in our
356              * case, this is TCP.
357              */
358             case CO_TL_ENTITY:
359                if (pcommand->toi_class == INFO_CLASS_GENERIC)
360                {
361                   if (!pcbResponseInfoLen)
362                      return ERROR_BAD_ENVIRONMENT;
363                   *((ULONG *)pResponseInfo) = CO_TL_TCP;
364                   *pcbResponseInfoLen = sizeof(ULONG);
365                }
366                else if (pcommand->toi_class == INFO_CLASS_PROTOCOL)
367                {
368                   if (!pcbResponseInfoLen)
369                      return ERROR_BAD_ENVIRONMENT;
370                   if (*pcbResponseInfoLen < sizeof(MIB_TCPSTATS))
371                      return ERROR_LOCK_VIOLATION;
372                   GetTcpStatistics((PMIB_TCPSTATS)pResponseInfo);
373                   *pcbResponseInfoLen = sizeof(MIB_TCPSTATS);
374                }
375                break;
376
377             default:
378                ERR("Unknown entity %ld for ENTITY_TYPE_ID query",
379                 pcommand->toi_entity.tei_entity);
380          }
381          break;
382
383          /* This call returns the IP address, subnet mask, and broadcast
384           * address for an interface.  If there are multiple IP addresses for
385           * the interface with the given index, returns the "first" one.
386           */
387          case IP_MIB_ADDRTABLE_ENTRY_ID:
388          {
389             DWORD index = pcommand->toi_entity.tei_instance;
390             PMIB_IPADDRROW baseIPInfo = (PMIB_IPADDRROW) pResponseInfo;
391             PMIB_IPADDRTABLE table;
392             DWORD tableSize, i;
393
394             if (!pcbResponseInfoLen)
395                return ERROR_BAD_ENVIRONMENT;
396             if (*pcbResponseInfoLen < sizeof(MIB_IPADDRROW))
397                return (ERROR_LOCK_VIOLATION);
398
399             /* get entire table, because there isn't an exported function that
400                gets just one entry. */
401             tableSize = 0;
402             GetIpAddrTable(NULL, &tableSize, FALSE);
403             table = (PMIB_IPADDRTABLE)calloc(1, tableSize);
404             if (!table)
405                return ERROR_NOT_ENOUGH_MEMORY;
406             GetIpAddrTable(table, &tableSize, FALSE);
407             for (i = 0; i < table->dwNumEntries; i++)
408             {
409                if (table->table[i].dwIndex == index)
410                {
411                   TRACE("Found IP info for tei_instance 0x%lx:\n", index);
412                   TRACE("IP 0x%08lx, mask 0x%08lx\n", table->table[i].dwAddr,
413                    table->table[i].dwMask);
414                   memcpy(baseIPInfo, &table->table[i], sizeof(MIB_IPADDRROW));
415                   break;
416                }
417             }
418             free(table);
419
420             *pcbResponseInfoLen = sizeof(MIB_IPADDRROW);
421             break;
422          }
423
424          /* FIXME: not real name. Value is 0x101.  Obviously it's a bad name,
425           * too, because it can be used to get the ARP table--see below. */
426          case IP_MIB_ROUTETABLE_ENTRY_ID:
427          {
428             switch (pcommand->toi_entity.tei_entity)
429             {
430             /* This call returns the routing table.
431              * No official documentation found, even the name of the command is unknown.
432              * Work is based on
433              * http://www.cyberport.com/~tangent/programming/winsock/articles/wscontrol.html
434              * and testings done with winipcfg.exe, route.exe and ipconfig.exe.
435              * pcommand->toi_entity.tei_instance seems to be the interface number
436              * but route.exe outputs only the information for the last interface
437              * if only the routes for the pcommand->toi_entity.tei_instance
438              * interface are returned. */
439                case CL_NL_ENTITY:
440                {
441                   DWORD routeTableSize, numRoutes, ndx;
442                   PMIB_IPFORWARDTABLE table;
443                   IPRouteEntry *winRouteTable  = (IPRouteEntry *) pResponseInfo;
444
445                   if (!pcbResponseInfoLen)
446                      return ERROR_BAD_ENVIRONMENT;
447                   GetIpForwardTable(NULL, &routeTableSize, FALSE);
448                   numRoutes = min(routeTableSize - sizeof(MIB_IPFORWARDTABLE),
449                    0) / sizeof(MIB_IPFORWARDROW) + 1;
450                   if (*pcbResponseInfoLen < sizeof(IPRouteEntry) * numRoutes)
451                      return (ERROR_LOCK_VIOLATION);
452                   table = (PMIB_IPFORWARDTABLE)calloc(1, routeTableSize);
453                   if (!table)
454                      return ERROR_NOT_ENOUGH_MEMORY;
455                   GetIpForwardTable(table, &routeTableSize, FALSE);
456
457                   memset(pResponseInfo, 0, sizeof(IPRouteEntry) * numRoutes);
458                   for (ndx = 0; ndx < table->dwNumEntries; ndx++)
459                   {
460                      winRouteTable->ire_addr = table->table[ndx].dwForwardDest;
461                      winRouteTable->ire_index =
462                       table->table[ndx].dwForwardIfIndex;
463                      winRouteTable->ire_metric =
464                       table->table[ndx].dwForwardMetric1;
465                      /* winRouteTable->ire_option4 =
466                      winRouteTable->ire_option5 =
467                      winRouteTable->ire_option6 = */
468                      winRouteTable->ire_gw = table->table[ndx].dwForwardNextHop;
469                      /* winRouteTable->ire_option8 =
470                      winRouteTable->ire_option9 =
471                      winRouteTable->ire_option10 = */
472                      winRouteTable->ire_mask = table->table[ndx].dwForwardMask;
473                      /* winRouteTable->ire_option12 = */
474                      winRouteTable++;
475                   }
476
477                   /* calculate the length of the data in the output buffer */
478                   *pcbResponseInfoLen = sizeof(IPRouteEntry) *
479                    table->dwNumEntries;
480
481                   free(table);
482                }
483                break;
484
485                case AT_ARP:
486                {
487                   DWORD arpTableSize, numEntries, ret;
488                   PMIB_IPNETTABLE table;
489
490                   if (!pcbResponseInfoLen)
491                      return ERROR_BAD_ENVIRONMENT;
492                   GetIpNetTable(NULL, &arpTableSize, FALSE);
493                   numEntries = min(arpTableSize - sizeof(MIB_IPNETTABLE),
494                    0) / sizeof(MIB_IPNETROW) + 1;
495                   if (*pcbResponseInfoLen < sizeof(MIB_IPNETROW) * numEntries)
496                      return (ERROR_LOCK_VIOLATION);
497                   table = (PMIB_IPNETTABLE)calloc(1, arpTableSize);
498                   if (!table)
499                      return ERROR_NOT_ENOUGH_MEMORY;
500                   ret = GetIpNetTable(table, &arpTableSize, FALSE);
501                   if (ret != NO_ERROR)
502                      return ret;
503                   if (*pcbResponseInfoLen < sizeof(MIB_IPNETROW) *
504                    table->dwNumEntries)
505                   {
506                      free(table);
507                      return ERROR_LOCK_VIOLATION;
508                   }
509                   memcpy(pResponseInfo, table->table, sizeof(MIB_IPNETROW) *
510                    table->dwNumEntries);
511
512                   /* calculate the length of the data in the output buffer */
513                   *pcbResponseInfoLen = sizeof(MIB_IPNETROW) *
514                    table->dwNumEntries;
515
516                   free(table);
517                }
518                break;
519
520                default:
521                {
522                   FIXME ("Command ID Not Supported -> toi_id=0x%lx, toi_entity={tei_entity=0x%lx, tei_instance=0x%lx}, toi_class=0x%lx\n",
523                      pcommand->toi_id, pcommand->toi_entity.tei_entity,
524                      pcommand->toi_entity.tei_instance, pcommand->toi_class);
525
526                   return (ERROR_BAD_ENVIRONMENT);
527                }
528             }
529          }
530          break;
531
532
533          default:
534          {
535             FIXME ("Command ID Not Supported -> toi_id=0x%lx, toi_entity={tei_entity=0x%lx, tei_instance=0x%lx}, toi_class=0x%lx\n",
536                pcommand->toi_id, pcommand->toi_entity.tei_entity,
537                pcommand->toi_entity.tei_instance, pcommand->toi_class);
538
539             return (ERROR_BAD_ENVIRONMENT);
540          }
541       }
542
543       break;
544    }
545
546    case WSCNTL_TCPIP_ICMP_ECHO:
547    {
548       unsigned int addr = *(unsigned int*)pRequestInfo;
549       #if 0
550          int timeout= *(unsigned int*)(inbuf+4);
551          short x1 = *(unsigned short*)(inbuf+8);
552          short sendbufsize = *(unsigned short*)(inbuf+10);
553          char x2 = *(unsigned char*)(inbuf+12);
554          char ttl = *(unsigned char*)(inbuf+13);
555          char service = *(unsigned char*)(inbuf+14);
556          char type= *(unsigned char*)(inbuf+15); /* 0x2: don't fragment*/
557       #endif
558
559       FIXME("(ICMP_ECHO) to 0x%08x stub \n", addr);
560       break;
561    }
562
563    default:
564       FIXME("Protocol Not Supported -> protocol=0x%lx, action=0x%lx, Request=%p, RequestLen=%p, Response=%p, ResponseLen=%p\n",
565        protocol, action, pRequestInfo, pcbRequestInfoLen, pResponseInfo, pcbResponseInfoLen);
566
567       return (WSAEOPNOTSUPP);
568
569    }
570
571    return (WSCTL_SUCCESS);
572 }
573
574
575
576 /***********************************************************************
577  *              WSARecvEx                       (WSOCK32.1107)
578  *
579  * WSARecvEx is a Microsoft specific extension to winsock that is identical to recv
580  * except that has an in/out argument call flags that has the value MSG_PARTIAL ored
581  * into the flags parameter when a partial packet is read. This only applies to
582  * sockets using the datagram protocol. This method does not seem to be implemented
583  * correctly by microsoft as the winsock implementation does not set the MSG_PARTIAL
584  * flag when a fragmented packet arrives.
585  */
586 INT WINAPI WSARecvEx(SOCKET s, char *buf, INT len, INT *flags)
587 {
588     FIXME("(WSARecvEx) partial packet return value not set \n");
589     return recv(s, buf, len, *flags);
590 }
591
592
593 /***********************************************************************
594  *       s_perror         (WSOCK32.1108)
595  */
596 void WINAPI s_perror(LPCSTR message)
597 {
598     FIXME("(%s): stub\n",message);
599     return;
600 }