RegQueryValueW must return count in bytes for empty string too.
[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\n",
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\n",
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\n",
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          case IP_MIB_TABLE_ENTRY_ID:
425          {
426             switch (pcommand->toi_entity.tei_entity)
427             {
428             /* This call returns the routing table.
429              * No official documentation found, even the name of the command is unknown.
430              * Work is based on
431              * http://www.cyberport.com/~tangent/programming/winsock/articles/wscontrol.html
432              * and testings done with winipcfg.exe, route.exe and ipconfig.exe.
433              * pcommand->toi_entity.tei_instance seems to be the interface number
434              * but route.exe outputs only the information for the last interface
435              * if only the routes for the pcommand->toi_entity.tei_instance
436              * interface are returned. */
437                case CL_NL_ENTITY:
438                {
439                   DWORD routeTableSize, numRoutes, ndx;
440                   PMIB_IPFORWARDTABLE table;
441                   IPRouteEntry *winRouteTable  = (IPRouteEntry *) pResponseInfo;
442
443                   if (!pcbResponseInfoLen)
444                      return ERROR_BAD_ENVIRONMENT;
445                   GetIpForwardTable(NULL, &routeTableSize, FALSE);
446                   numRoutes = min(routeTableSize - sizeof(MIB_IPFORWARDTABLE),
447                    0) / sizeof(MIB_IPFORWARDROW) + 1;
448                   if (*pcbResponseInfoLen < sizeof(IPRouteEntry) * numRoutes)
449                      return (ERROR_LOCK_VIOLATION);
450                   table = (PMIB_IPFORWARDTABLE)calloc(1, routeTableSize);
451                   if (!table)
452                      return ERROR_NOT_ENOUGH_MEMORY;
453                   GetIpForwardTable(table, &routeTableSize, FALSE);
454
455                   memset(pResponseInfo, 0, sizeof(IPRouteEntry) * numRoutes);
456                   for (ndx = 0; ndx < table->dwNumEntries; ndx++)
457                   {
458                      winRouteTable->ire_addr = table->table[ndx].dwForwardDest;
459                      winRouteTable->ire_index =
460                       table->table[ndx].dwForwardIfIndex;
461                      winRouteTable->ire_metric =
462                       table->table[ndx].dwForwardMetric1;
463                      /* winRouteTable->ire_option4 =
464                      winRouteTable->ire_option5 =
465                      winRouteTable->ire_option6 = */
466                      winRouteTable->ire_gw = table->table[ndx].dwForwardNextHop;
467                      /* winRouteTable->ire_option8 =
468                      winRouteTable->ire_option9 =
469                      winRouteTable->ire_option10 = */
470                      winRouteTable->ire_mask = table->table[ndx].dwForwardMask;
471                      /* winRouteTable->ire_option12 = */
472                      winRouteTable++;
473                   }
474
475                   /* calculate the length of the data in the output buffer */
476                   *pcbResponseInfoLen = sizeof(IPRouteEntry) *
477                    table->dwNumEntries;
478
479                   free(table);
480                }
481                break;
482
483                case AT_ARP:
484                {
485                   DWORD arpTableSize, numEntries, ret;
486                   PMIB_IPNETTABLE table;
487
488                   if (!pcbResponseInfoLen)
489                      return ERROR_BAD_ENVIRONMENT;
490                   GetIpNetTable(NULL, &arpTableSize, FALSE);
491                   numEntries = min(arpTableSize - sizeof(MIB_IPNETTABLE),
492                    0) / sizeof(MIB_IPNETROW) + 1;
493                   if (*pcbResponseInfoLen < sizeof(MIB_IPNETROW) * numEntries)
494                      return (ERROR_LOCK_VIOLATION);
495                   table = (PMIB_IPNETTABLE)calloc(1, arpTableSize);
496                   if (!table)
497                      return ERROR_NOT_ENOUGH_MEMORY;
498                   ret = GetIpNetTable(table, &arpTableSize, FALSE);
499                   if (ret != NO_ERROR)
500                      return ret;
501                   if (*pcbResponseInfoLen < sizeof(MIB_IPNETROW) *
502                    table->dwNumEntries)
503                   {
504                      free(table);
505                      return ERROR_LOCK_VIOLATION;
506                   }
507                   memcpy(pResponseInfo, table->table, sizeof(MIB_IPNETROW) *
508                    table->dwNumEntries);
509
510                   /* calculate the length of the data in the output buffer */
511                   *pcbResponseInfoLen = sizeof(MIB_IPNETROW) *
512                    table->dwNumEntries;
513
514                   free(table);
515                }
516                break;
517
518                case CO_TL_ENTITY:
519                {
520                   DWORD tcpTableSize, numEntries, ret;
521                   PMIB_TCPTABLE table;
522                   DWORD i;
523
524                   if (!pcbResponseInfoLen)
525                      return ERROR_BAD_ENVIRONMENT;
526                   GetTcpTable(NULL, &tcpTableSize, FALSE);
527                   numEntries = min(tcpTableSize - sizeof(MIB_TCPTABLE),
528                    0) / sizeof(MIB_TCPROW) + 1;
529                   if (*pcbResponseInfoLen < sizeof(MIB_TCPROW) * numEntries)
530                      return (ERROR_LOCK_VIOLATION);
531                   table = (PMIB_TCPTABLE)calloc(1, tcpTableSize);
532                   if (!table)
533                      return ERROR_NOT_ENOUGH_MEMORY;
534                   ret = GetTcpTable(table, &tcpTableSize, FALSE);
535                   if (ret != NO_ERROR)
536                      return ret;
537                   if (*pcbResponseInfoLen < sizeof(MIB_TCPROW) *
538                    table->dwNumEntries)
539                   {
540                      free(table);
541                      return ERROR_LOCK_VIOLATION;
542                   }
543                   for (i = 0; i < table->dwNumEntries; i++)
544                   {
545                      USHORT sPort;
546
547                      sPort = ntohs((USHORT)table->table[i].dwLocalPort);
548                      table->table[i].dwLocalPort = (DWORD)sPort;
549                      sPort = ntohs((USHORT)table->table[i].dwRemotePort);
550                      table->table[i].dwRemotePort = (DWORD)sPort;
551                   }
552                   memcpy(pResponseInfo, table->table, sizeof(MIB_TCPROW) *
553                    table->dwNumEntries);
554
555                   /* calculate the length of the data in the output buffer */
556                   *pcbResponseInfoLen = sizeof(MIB_TCPROW) *
557                    table->dwNumEntries;
558
559                   free(table);
560                }
561                break;
562
563                default:
564                {
565                   FIXME ("Command ID Not Supported -> toi_id=0x%lx, toi_entity={tei_entity=0x%lx, tei_instance=0x%lx}, toi_class=0x%lx\n",
566                      pcommand->toi_id, pcommand->toi_entity.tei_entity,
567                      pcommand->toi_entity.tei_instance, pcommand->toi_class);
568
569                   return (ERROR_BAD_ENVIRONMENT);
570                }
571             }
572          }
573          break;
574
575
576          default:
577          {
578             FIXME ("Command ID Not Supported -> toi_id=0x%lx, toi_entity={tei_entity=0x%lx, tei_instance=0x%lx}, toi_class=0x%lx\n",
579                pcommand->toi_id, pcommand->toi_entity.tei_entity,
580                pcommand->toi_entity.tei_instance, pcommand->toi_class);
581
582             return (ERROR_BAD_ENVIRONMENT);
583          }
584       }
585
586       break;
587    }
588
589    case WSCNTL_TCPIP_ICMP_ECHO:
590    {
591       unsigned int addr = *(unsigned int*)pRequestInfo;
592       #if 0
593          int timeout= *(unsigned int*)(inbuf+4);
594          short x1 = *(unsigned short*)(inbuf+8);
595          short sendbufsize = *(unsigned short*)(inbuf+10);
596          char x2 = *(unsigned char*)(inbuf+12);
597          char ttl = *(unsigned char*)(inbuf+13);
598          char service = *(unsigned char*)(inbuf+14);
599          char type= *(unsigned char*)(inbuf+15); /* 0x2: don't fragment*/
600       #endif
601
602       FIXME("(ICMP_ECHO) to 0x%08x stub \n", addr);
603       break;
604    }
605
606    default:
607       FIXME("Protocol Not Supported -> protocol=0x%lx, action=0x%lx, Request=%p, RequestLen=%p, Response=%p, ResponseLen=%p\n",
608        protocol, action, pRequestInfo, pcbRequestInfoLen, pResponseInfo, pcbResponseInfoLen);
609
610       return (WSAEOPNOTSUPP);
611
612    }
613
614    return (WSCTL_SUCCESS);
615 }
616
617
618
619 /***********************************************************************
620  *              WSARecvEx                       (WSOCK32.1107)
621  *
622  * WSARecvEx is a Microsoft specific extension to winsock that is identical to recv
623  * except that has an in/out argument call flags that has the value MSG_PARTIAL ored
624  * into the flags parameter when a partial packet is read. This only applies to
625  * sockets using the datagram protocol. This method does not seem to be implemented
626  * correctly by microsoft as the winsock implementation does not set the MSG_PARTIAL
627  * flag when a fragmented packet arrives.
628  */
629 INT WINAPI WSARecvEx(SOCKET s, char *buf, INT len, INT *flags)
630 {
631     FIXME("(WSARecvEx) partial packet return value not set \n");
632     return recv(s, buf, len, *flags);
633 }
634
635
636 /***********************************************************************
637  *       s_perror         (WSOCK32.1108)
638  */
639 void WINAPI s_perror(LPCSTR message)
640 {
641     FIXME("(%s): stub\n",message);
642     return;
643 }