msvcrt: Allow forwarding to stub functions when the arguments are known.
[wine] / dlls / inetmib1 / main.c
1 /*
2  * Copyright 2008 Juan Lang
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include "config.h"
20 #include <assert.h>
21 #include <stdarg.h>
22 #include <stdlib.h>
23 #include <limits.h>
24 #include "windef.h"
25 #include "winbase.h"
26 #include "snmp.h"
27 #include "iphlpapi.h"
28 #include "wine/debug.h"
29
30 WINE_DEFAULT_DEBUG_CHANNEL(inetmib1);
31
32 /**
33  * Utility functions
34  */
35 static DWORD copyInt(AsnAny *value, void *src)
36 {
37     value->asnType = ASN_INTEGER;
38     value->asnValue.number = *(DWORD *)src;
39     return SNMP_ERRORSTATUS_NOERROR;
40 }
41
42 static void setStringValue(AsnAny *value, BYTE type, DWORD len, BYTE *str)
43 {
44     AsnAny strValue;
45
46     strValue.asnType = type;
47     strValue.asnValue.string.stream = str;
48     strValue.asnValue.string.length = len;
49     strValue.asnValue.string.dynamic = FALSE;
50     SnmpUtilAsnAnyCpy(value, &strValue);
51 }
52
53 typedef DWORD (*copyValueFunc)(AsnAny *value, void *src);
54
55 struct structToAsnValue
56 {
57     size_t        offset;
58     copyValueFunc copy;
59 };
60
61 static AsnInteger32 mapStructEntryToValue(struct structToAsnValue *map,
62     UINT mapLen, void *record, UINT id, BYTE bPduType, SnmpVarBind *pVarBind)
63 {
64     /* OIDs are 1-based */
65     if (!id)
66         return SNMP_ERRORSTATUS_NOSUCHNAME;
67     --id;
68     if (id >= mapLen)
69         return SNMP_ERRORSTATUS_NOSUCHNAME;
70     if (!map[id].copy)
71         return SNMP_ERRORSTATUS_NOSUCHNAME;
72     return map[id].copy(&pVarBind->value, (BYTE *)record + map[id].offset);
73 }
74
75 static DWORD copyIpAddr(AsnAny *value, void *src)
76 {
77     setStringValue(value, ASN_IPADDRESS, sizeof(DWORD), src);
78     return SNMP_ERRORSTATUS_NOERROR;
79 }
80
81 static UINT mib2[] = { 1,3,6,1,2,1 };
82 static UINT mib2System[] = { 1,3,6,1,2,1,1 };
83
84 typedef BOOL (*varqueryfunc)(BYTE bPduType, SnmpVarBind *pVarBind,
85     AsnInteger32 *pErrorStatus);
86
87 struct mibImplementation
88 {
89     AsnObjectIdentifier name;
90     void              (*init)(void);
91     varqueryfunc        query;
92     void              (*cleanup)(void);
93 };
94
95 static UINT mib2IfNumber[] = { 1,3,6,1,2,1,2,1 };
96 static PMIB_IFTABLE ifTable;
97
98 static void mib2IfNumberInit(void)
99 {
100     DWORD size = 0, ret = GetIfTable(NULL, &size, FALSE);
101
102     if (ret == ERROR_INSUFFICIENT_BUFFER)
103     {
104         MIB_IFTABLE *table = HeapAlloc(GetProcessHeap(), 0, size);
105         if (table)
106         {
107             if (!GetIfTable(table, &size, FALSE)) ifTable = table;
108             else HeapFree(GetProcessHeap(), 0, table );
109         }
110     }
111 }
112
113 static void mib2IfNumberCleanup(void)
114 {
115     HeapFree(GetProcessHeap(), 0, ifTable);
116 }
117
118 static BOOL mib2IfNumberQuery(BYTE bPduType, SnmpVarBind *pVarBind,
119     AsnInteger32 *pErrorStatus)
120 {
121     AsnObjectIdentifier numberOid = DEFINE_OID(mib2IfNumber);
122     BOOL ret = TRUE;
123
124     TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name),
125         pErrorStatus);
126
127     switch (bPduType)
128     {
129     case SNMP_PDU_GET:
130     case SNMP_PDU_GETNEXT:
131         if ((bPduType == SNMP_PDU_GET &&
132             !SnmpUtilOidNCmp(&pVarBind->name, &numberOid, numberOid.idLength))
133             || SnmpUtilOidNCmp(&pVarBind->name, &numberOid, numberOid.idLength)
134             < 0)
135         {
136             DWORD numIfs = ifTable ? ifTable->dwNumEntries : 0;
137
138             copyInt(&pVarBind->value, &numIfs);
139             if (bPduType == SNMP_PDU_GETNEXT)
140             {
141                 SnmpUtilOidFree(&pVarBind->name);
142                 SnmpUtilOidCpy(&pVarBind->name, &numberOid);
143             }
144             *pErrorStatus = SNMP_ERRORSTATUS_NOERROR;
145         }
146         else
147         {
148             *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
149             /* Caller deals with OID if bPduType == SNMP_PDU_GETNEXT, so don't
150              * need to set it here.
151              */
152         }
153         break;
154     case SNMP_PDU_SET:
155         *pErrorStatus = SNMP_ERRORSTATUS_READONLY;
156         ret = FALSE;
157         break;
158     default:
159         FIXME("0x%02x: unsupported PDU type\n", bPduType);
160         *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
161     }
162     return ret;
163 }
164
165 static DWORD copyOperStatus(AsnAny *value, void *src)
166 {
167     value->asnType = ASN_INTEGER;
168     /* The IPHlpApi definition of operational status differs from the MIB2 one,
169      * so map it to the MIB2 value.
170      */
171     switch (*(DWORD *)src)
172     {
173     case MIB_IF_OPER_STATUS_OPERATIONAL:
174         value->asnValue.number = MIB_IF_ADMIN_STATUS_UP;
175         break;
176     case MIB_IF_OPER_STATUS_CONNECTING:
177     case MIB_IF_OPER_STATUS_CONNECTED:
178         value->asnValue.number = MIB_IF_ADMIN_STATUS_TESTING;
179         break;
180     default:
181         value->asnValue.number = MIB_IF_ADMIN_STATUS_DOWN;
182     };
183     return SNMP_ERRORSTATUS_NOERROR;
184 }
185
186 /* Given an OID and a base OID that it must begin with, finds the item and
187  * integer instance from the OID.  E.g., given an OID foo.1.2 and a base OID
188  * foo, returns item 1 and instance 2.
189  * If bPduType is not SNMP_PDU_GETNEXT and either the item or instance is
190  * missing, returns SNMP_ERRORSTATUS_NOSUCHNAME.
191  * If bPduType is SNMP_PDU_GETNEXT, returns the successor to the item and
192  * instance, or item 1, instance 1 if either is missing.
193  */
194 static AsnInteger32 getItemAndIntegerInstanceFromOid(AsnObjectIdentifier *oid,
195     AsnObjectIdentifier *base, BYTE bPduType, UINT *item, UINT *instance)
196 {
197     AsnInteger32 ret = SNMP_ERRORSTATUS_NOERROR;
198
199     switch (bPduType)
200     {
201     case SNMP_PDU_GETNEXT:
202         if (SnmpUtilOidNCmp(oid, base, base->idLength) < 0)
203         {
204             *item = 1;
205             *instance = 1;
206         }
207         else if (!SnmpUtilOidNCmp(oid, base, base->idLength))
208         {
209             if (oid->idLength == base->idLength ||
210                 oid->idLength == base->idLength + 1)
211             {
212                 /* Either the table or an item within the table is specified,
213                  * but the instance is not.  Get the first instance.
214                  */
215                 *instance = 1;
216                 if (oid->idLength == base->idLength + 1)
217                     *item = oid->ids[base->idLength];
218                 else
219                     *item = 1;
220             }
221             else
222             {
223                 *item = oid->ids[base->idLength];
224                 *instance = oid->ids[base->idLength + 1] + 1;
225             }
226         }
227         else
228             ret = SNMP_ERRORSTATUS_NOSUCHNAME;
229         break;
230     default:
231         if (!SnmpUtilOidNCmp(oid, base, base->idLength))
232         {
233             if (oid->idLength == base->idLength ||
234                 oid->idLength == base->idLength + 1)
235             {
236                 /* Either the table or an item within the table is specified,
237                  * but the instance is not.
238                  */
239                 ret = SNMP_ERRORSTATUS_NOSUCHNAME;
240             }
241             else
242             {
243                 *item = oid->ids[base->idLength];
244                 *instance = oid->ids[base->idLength + 1];
245             }
246         }
247         else
248             ret = SNMP_ERRORSTATUS_NOSUCHNAME;
249     }
250     return ret;
251 }
252
253 /* Given an OID and a base OID that it must begin with, finds the item from the
254  * OID.  E.g., given an OID foo.1 and a base OID foo, returns item 1.
255  * If bPduType is not SNMP_PDU_GETNEXT and the item is missing, returns
256  * SNMP_ERRORSTATUS_NOSUCHNAME.
257  * If bPduType is SNMP_PDU_GETNEXT, returns the successor to the item, or item
258  * 1 if the item is missing.
259  */
260 static AsnInteger32 getItemFromOid(AsnObjectIdentifier *oid,
261     AsnObjectIdentifier *base, BYTE bPduType, UINT *item)
262 {
263     AsnInteger32 ret = SNMP_ERRORSTATUS_NOERROR;
264
265     switch (bPduType)
266     {
267     case SNMP_PDU_GETNEXT:
268         if (SnmpUtilOidNCmp(oid, base, base->idLength) < 0)
269             *item = 1;
270         else if (!SnmpUtilOidNCmp(oid, base, base->idLength))
271         {
272             if (oid->idLength == base->idLength)
273             {
274                 /* The item is missing, assume the first item */
275                 *item = 1;
276             }
277             else
278                 *item = oid->ids[base->idLength] + 1;
279         }
280         else
281             ret = SNMP_ERRORSTATUS_NOSUCHNAME;
282         break;
283     default:
284         if (!SnmpUtilOidNCmp(oid, base, base->idLength))
285         {
286             if (oid->idLength == base->idLength)
287             {
288                 /* The item is missing */
289                 ret = SNMP_ERRORSTATUS_NOSUCHNAME;
290             }
291             else
292             {
293                 *item = oid->ids[base->idLength];
294                 if (!*item)
295                     ret = SNMP_ERRORSTATUS_NOSUCHNAME;
296             }
297         }
298         else
299             ret = SNMP_ERRORSTATUS_NOSUCHNAME;
300     }
301     return ret;
302 }
303
304 struct GenericTable
305 {
306     DWORD numEntries;
307     BYTE  entries[1];
308 };
309
310 static DWORD oidToIpAddr(AsnObjectIdentifier *oid)
311 {
312     assert(oid && oid->idLength >= 4);
313     /* Map the IDs to an IP address in little-endian order */
314     return (BYTE)oid->ids[3] << 24 | (BYTE)oid->ids[2] << 16 |
315         (BYTE)oid->ids[1] << 8 | (BYTE)oid->ids[0];
316 }
317
318 typedef void (*oidToKeyFunc)(AsnObjectIdentifier *oid, void *dst);
319 typedef int (*compareFunc)(const void *key, const void *value);
320
321 /* Finds the first value in the table that matches key.  Returns its 1-based
322  * index if found, or 0 if not found.
323  */
324 static UINT findValueInTable(const void *key,
325     struct GenericTable *table, size_t tableEntrySize, compareFunc compare)
326 {
327     UINT index = 0;
328     void *value;
329
330     value = bsearch(key, table->entries, table->numEntries, tableEntrySize,
331         compare);
332     if (value)
333         index = ((BYTE *)value - (BYTE *)table->entries) / tableEntrySize + 1;
334     return index;
335 }
336
337 /* Finds the first value in the table that matches oid, using makeKey to
338  * convert the oid to a key for comparison.  Returns the value's 1-based
339  * index if found, or 0 if not found.
340  */
341 static UINT findOidInTable(AsnObjectIdentifier *oid,
342     struct GenericTable *table, size_t tableEntrySize, oidToKeyFunc makeKey,
343     compareFunc compare)
344 {
345     UINT index = 0;
346     void *key = HeapAlloc(GetProcessHeap(), 0, tableEntrySize);
347
348     if (key)
349     {
350         makeKey(oid, key);
351         index = findValueInTable(key, table, tableEntrySize, compare);
352         HeapFree(GetProcessHeap(), 0, key);
353     }
354     return index;
355 }
356
357 /* Finds the first successor to the value in the table that does matches oid,
358  * using makeKey to convert the oid to a key for comparison.  A successor is
359  * a value that does not match oid, so if multiple entries match an oid, only
360  * the first will ever be returned using this method.
361  * Returns the successor's 1-based index if found, or 0 if not found.
362  */
363 static UINT findNextOidInTable(AsnObjectIdentifier *oid,
364     struct GenericTable *table, size_t tableEntrySize, oidToKeyFunc makeKey,
365     compareFunc compare)
366 {
367     UINT index = 0;
368     void *key = HeapAlloc(GetProcessHeap(), 0, tableEntrySize);
369
370     if (key)
371     {
372         makeKey(oid, key);
373         index = findValueInTable(key, table, tableEntrySize, compare);
374         if (index == 0)
375         {
376             /* Not found in table.  If it's less than the first entry, return
377              * the first index.  Otherwise just return 0 and let the caller
378              * handle finding the successor.
379              */
380             if (compare(key, table->entries) < 0)
381                 index = 1;
382         }
383         else
384         {
385             /* Skip any entries that match the same key.  This enumeration will
386              * be incomplete, but it's what Windows appears to do if there are
387              * multiple entries with the same index in a table, and it avoids
388              * an infinite loop.
389              */
390             for (++index; index <= table->numEntries && compare(key,
391                 &table->entries[tableEntrySize * (index - 1)]) == 0; ++index)
392                 ;
393         }
394         HeapFree(GetProcessHeap(), 0, key);
395     }
396     return index;
397 }
398
399 /* Given an OID and a base OID that it must begin with, finds the item and
400  * element of the table whose value matches the instance from the OID.
401  * The OID is converted to a key with the function makeKey, and compared
402  * against entries in the table with the function compare.
403  * If bPduType is not SNMP_PDU_GETNEXT and either the item or instance is
404  * missing, returns SNMP_ERRORSTATUS_NOSUCHNAME.
405  * If bPduType is SNMP_PDU_GETNEXT, returns the successor to the item and
406  * instance, or item 1, instance 1 if either is missing.
407  */
408 static AsnInteger32 getItemAndInstanceFromTable(AsnObjectIdentifier *oid,
409     AsnObjectIdentifier *base, UINT instanceLen, BYTE bPduType,
410     struct GenericTable *table, size_t tableEntrySize, oidToKeyFunc makeKey,
411     compareFunc compare, UINT *item, UINT *instance)
412 {
413     AsnInteger32 ret = SNMP_ERRORSTATUS_NOERROR;
414
415     if (!table)
416         return SNMP_ERRORSTATUS_NOSUCHNAME;
417
418     switch (bPduType)
419     {
420     case SNMP_PDU_GETNEXT:
421         if (SnmpUtilOidNCmp(oid, base, base->idLength) < 0)
422         {
423             /* Return the first item and instance from the table */
424             *item = 1;
425             *instance = 1;
426         }
427         else if (!SnmpUtilOidNCmp(oid, base, base->idLength) &&
428             oid->idLength < base->idLength + instanceLen + 1)
429         {
430             /* Either the table or an item is specified, but the instance is
431              * not.
432              */
433             *instance = 1;
434             if (oid->idLength >= base->idLength + 1)
435             {
436                 *item = oid->ids[base->idLength];
437                 if (!*item)
438                     *item = 1;
439             }
440             else
441                 *item = 1;
442         }
443         else if (!SnmpUtilOidNCmp(oid, base, base->idLength) &&
444             oid->idLength == base->idLength + instanceLen + 1)
445         {
446             *item = oid->ids[base->idLength];
447             if (!*item)
448             {
449                 *instance = 1;
450                 *item = 1;
451             }
452             else
453             {
454                 AsnObjectIdentifier instanceOid = { instanceLen,
455                     oid->ids + base->idLength + 1 };
456
457                 *instance = findNextOidInTable(&instanceOid, table,
458                     tableEntrySize, makeKey, compare);
459                 if (!*instance || *instance > table->numEntries)
460                     ret = SNMP_ERRORSTATUS_NOSUCHNAME;
461             }
462         }
463         else
464             ret = SNMP_ERRORSTATUS_NOSUCHNAME;
465         break;
466     default:
467         if (!SnmpUtilOidNCmp(oid, base, base->idLength) &&
468             oid->idLength == base->idLength + instanceLen + 1)
469         {
470             *item = oid->ids[base->idLength];
471             if (!*item)
472                 ret = SNMP_ERRORSTATUS_NOSUCHNAME;
473             else
474             {
475                 AsnObjectIdentifier instanceOid = { instanceLen,
476                     oid->ids + base->idLength + 1 };
477
478                 *instance = findOidInTable(&instanceOid, table, tableEntrySize,
479                     makeKey, compare);
480                 if (!*instance)
481                     ret = SNMP_ERRORSTATUS_NOSUCHNAME;
482             }
483         }
484         else
485             ret = SNMP_ERRORSTATUS_NOSUCHNAME;
486     }
487     return ret;
488 }
489
490 static INT setOidWithItem(AsnObjectIdentifier *dst, AsnObjectIdentifier *base,
491     UINT item)
492 {
493     UINT id;
494     AsnObjectIdentifier oid;
495     INT ret;
496
497     SnmpUtilOidFree(dst);
498     ret = SnmpUtilOidCpy(dst, base);
499     if (ret)
500     {
501         oid.idLength = 1;
502         oid.ids = &id;
503         id = item;
504         ret = SnmpUtilOidAppend(dst, &oid);
505     }
506     return ret;
507 }
508
509 static INT setOidWithItemAndIpAddr(AsnObjectIdentifier *dst,
510     AsnObjectIdentifier *base, UINT item, DWORD addr)
511 {
512     UINT id;
513     BYTE *ptr;
514     AsnObjectIdentifier oid;
515     INT ret;
516
517     ret = setOidWithItem(dst, base, item);
518     if (ret)
519     {
520         oid.idLength = 1;
521         oid.ids = &id;
522         for (ptr = (BYTE *)&addr; ret && ptr < (BYTE *)&addr + sizeof(DWORD);
523          ptr++)
524         {
525             id = *ptr;
526             ret = SnmpUtilOidAppend(dst, &oid);
527         }
528     }
529     return ret;
530 }
531
532 static INT setOidWithItemAndInteger(AsnObjectIdentifier *dst,
533     AsnObjectIdentifier *base, UINT item, UINT instance)
534 {
535     AsnObjectIdentifier oid;
536     INT ret;
537
538     ret = setOidWithItem(dst, base, item);
539     if (ret)
540     {
541         oid.idLength = 1;
542         oid.ids = &instance;
543         ret = SnmpUtilOidAppend(dst, &oid);
544     }
545     return ret;
546 }
547
548 static DWORD copyIfRowDescr(AsnAny *value, void *src)
549 {
550     PMIB_IFROW row = (PMIB_IFROW)((BYTE *)src -
551                                   FIELD_OFFSET(MIB_IFROW, dwDescrLen));
552     DWORD ret;
553
554     if (row->dwDescrLen)
555     {
556         setStringValue(value, ASN_OCTETSTRING, row->dwDescrLen, row->bDescr);
557         ret = SNMP_ERRORSTATUS_NOERROR;
558     }
559     else
560         ret = SNMP_ERRORSTATUS_NOSUCHNAME;
561     return ret;
562 }
563
564 static DWORD copyIfRowPhysAddr(AsnAny *value, void *src)
565 {
566     PMIB_IFROW row = (PMIB_IFROW)((BYTE *)src -
567                                   FIELD_OFFSET(MIB_IFROW, dwPhysAddrLen));
568     DWORD ret;
569
570     if (row->dwPhysAddrLen)
571     {
572         setStringValue(value, ASN_OCTETSTRING, row->dwPhysAddrLen,
573                        row->bPhysAddr);
574         ret = SNMP_ERRORSTATUS_NOERROR;
575     }
576     else
577         ret = SNMP_ERRORSTATUS_NOSUCHNAME;
578     return ret;
579 }
580
581 static struct structToAsnValue mib2IfEntryMap[] = {
582     { FIELD_OFFSET(MIB_IFROW, dwIndex), copyInt },
583     { FIELD_OFFSET(MIB_IFROW, dwDescrLen), copyIfRowDescr },
584     { FIELD_OFFSET(MIB_IFROW, dwType), copyInt },
585     { FIELD_OFFSET(MIB_IFROW, dwMtu), copyInt },
586     { FIELD_OFFSET(MIB_IFROW, dwSpeed), copyInt },
587     { FIELD_OFFSET(MIB_IFROW, dwPhysAddrLen), copyIfRowPhysAddr },
588     { FIELD_OFFSET(MIB_IFROW, dwAdminStatus), copyInt },
589     { FIELD_OFFSET(MIB_IFROW, dwOperStatus), copyOperStatus },
590     { FIELD_OFFSET(MIB_IFROW, dwLastChange), copyInt },
591     { FIELD_OFFSET(MIB_IFROW, dwInOctets), copyInt },
592     { FIELD_OFFSET(MIB_IFROW, dwInUcastPkts), copyInt },
593     { FIELD_OFFSET(MIB_IFROW, dwInNUcastPkts), copyInt },
594     { FIELD_OFFSET(MIB_IFROW, dwInDiscards), copyInt },
595     { FIELD_OFFSET(MIB_IFROW, dwInErrors), copyInt },
596     { FIELD_OFFSET(MIB_IFROW, dwInUnknownProtos), copyInt },
597     { FIELD_OFFSET(MIB_IFROW, dwOutOctets), copyInt },
598     { FIELD_OFFSET(MIB_IFROW, dwOutUcastPkts), copyInt },
599     { FIELD_OFFSET(MIB_IFROW, dwOutNUcastPkts), copyInt },
600     { FIELD_OFFSET(MIB_IFROW, dwOutDiscards), copyInt },
601     { FIELD_OFFSET(MIB_IFROW, dwOutErrors), copyInt },
602     { FIELD_OFFSET(MIB_IFROW, dwOutQLen), copyInt },
603 };
604
605 static UINT mib2IfEntry[] = { 1,3,6,1,2,1,2,2,1 };
606
607 static BOOL mib2IfEntryQuery(BYTE bPduType, SnmpVarBind *pVarBind,
608     AsnInteger32 *pErrorStatus)
609 {
610     AsnObjectIdentifier entryOid = DEFINE_OID(mib2IfEntry);
611     BOOL ret = TRUE;
612
613     TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name),
614         pErrorStatus);
615
616     switch (bPduType)
617     {
618     case SNMP_PDU_GET:
619     case SNMP_PDU_GETNEXT:
620         if (!ifTable)
621         {
622             /* There is no interface present, so let the caller deal
623              * with finding the successor.
624              */
625             *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
626         }
627         else
628         {
629             UINT tableIndex = 0, item = 0;
630
631             *pErrorStatus = getItemAndIntegerInstanceFromOid(&pVarBind->name,
632                 &entryOid, bPduType, &item, &tableIndex);
633             if (!*pErrorStatus)
634             {
635                 assert(tableIndex);
636                 assert(item);
637                 if (tableIndex > ifTable->dwNumEntries)
638                     *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
639                 else
640                 {
641                     *pErrorStatus = mapStructEntryToValue(mib2IfEntryMap,
642                         DEFINE_SIZEOF(mib2IfEntryMap),
643                         &ifTable->table[tableIndex - 1], item, bPduType,
644                         pVarBind);
645                     if (bPduType == SNMP_PDU_GETNEXT)
646                         ret = setOidWithItemAndInteger(&pVarBind->name,
647                             &entryOid, item, tableIndex);
648                 }
649             }
650         }
651         break;
652     case SNMP_PDU_SET:
653         *pErrorStatus = SNMP_ERRORSTATUS_READONLY;
654         ret = FALSE;
655         break;
656     default:
657         FIXME("0x%02x: unsupported PDU type\n", bPduType);
658         *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
659     }
660     return ret;
661 }
662
663 static UINT mib2Ip[] = { 1,3,6,1,2,1,4 };
664 static MIB_IPSTATS ipStats;
665
666 static void mib2IpStatsInit(void)
667 {
668     GetIpStatistics(&ipStats);
669 }
670
671 static struct structToAsnValue mib2IpMap[] = {
672     { FIELD_OFFSET(MIB_IPSTATS, dwForwarding), copyInt }, /* 1 */
673     { FIELD_OFFSET(MIB_IPSTATS, dwDefaultTTL), copyInt }, /* 2 */
674     { FIELD_OFFSET(MIB_IPSTATS, dwInReceives), copyInt }, /* 3 */
675     { FIELD_OFFSET(MIB_IPSTATS, dwInHdrErrors), copyInt }, /* 4 */
676     { FIELD_OFFSET(MIB_IPSTATS, dwInAddrErrors), copyInt }, /* 5 */
677     { FIELD_OFFSET(MIB_IPSTATS, dwForwDatagrams), copyInt }, /* 6 */
678     { FIELD_OFFSET(MIB_IPSTATS, dwInUnknownProtos), copyInt }, /* 7 */
679     { FIELD_OFFSET(MIB_IPSTATS, dwInDiscards), copyInt }, /* 8 */
680     { FIELD_OFFSET(MIB_IPSTATS, dwInDelivers), copyInt }, /* 9 */
681     { FIELD_OFFSET(MIB_IPSTATS, dwOutRequests), copyInt }, /* 10 */
682     { FIELD_OFFSET(MIB_IPSTATS, dwOutDiscards), copyInt }, /* 11 */
683     { FIELD_OFFSET(MIB_IPSTATS, dwOutNoRoutes), copyInt }, /* 12 */
684     { FIELD_OFFSET(MIB_IPSTATS, dwReasmTimeout), copyInt }, /* 13 */
685     { FIELD_OFFSET(MIB_IPSTATS, dwReasmReqds), copyInt }, /* 14 */
686     { FIELD_OFFSET(MIB_IPSTATS, dwReasmOks), copyInt }, /* 15 */
687     { FIELD_OFFSET(MIB_IPSTATS, dwReasmFails), copyInt }, /* 16 */
688     { FIELD_OFFSET(MIB_IPSTATS, dwFragOks), copyInt }, /* 17 */
689     { FIELD_OFFSET(MIB_IPSTATS, dwFragFails), copyInt }, /* 18 */
690     { FIELD_OFFSET(MIB_IPSTATS, dwFragCreates), copyInt }, /* 19 */
691     { 0, NULL }, /* 20: not used, IP addr table */
692     { 0, NULL }, /* 21: not used, route table */
693     { 0, NULL }, /* 22: not used, net to media (ARP) table */
694     { FIELD_OFFSET(MIB_IPSTATS, dwRoutingDiscards), copyInt }, /* 23 */
695 };
696
697 static BOOL mib2IpStatsQuery(BYTE bPduType, SnmpVarBind *pVarBind,
698     AsnInteger32 *pErrorStatus)
699 {
700     AsnObjectIdentifier myOid = DEFINE_OID(mib2Ip);
701     UINT item = 0;
702     BOOL ret = TRUE;
703
704     TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name),
705         pErrorStatus);
706
707     switch (bPduType)
708     {
709     case SNMP_PDU_GET:
710     case SNMP_PDU_GETNEXT:
711         *pErrorStatus = getItemFromOid(&pVarBind->name, &myOid, bPduType,
712             &item);
713         if (!*pErrorStatus)
714         {
715             *pErrorStatus = mapStructEntryToValue(mib2IpMap,
716                 DEFINE_SIZEOF(mib2IpMap), &ipStats, item, bPduType, pVarBind);
717             if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT)
718                 ret = setOidWithItem(&pVarBind->name, &myOid, item);
719         }
720         break;
721     case SNMP_PDU_SET:
722         *pErrorStatus = SNMP_ERRORSTATUS_READONLY;
723         ret = FALSE;
724         break;
725     default:
726         FIXME("0x%02x: unsupported PDU type\n", bPduType);
727         *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
728     }
729     return ret;
730 }
731
732 static UINT mib2IpAddr[] = { 1,3,6,1,2,1,4,20,1 };
733 static PMIB_IPADDRTABLE ipAddrTable;
734
735 static struct structToAsnValue mib2IpAddrMap[] = {
736     { FIELD_OFFSET(MIB_IPADDRROW, dwAddr), copyIpAddr },
737     { FIELD_OFFSET(MIB_IPADDRROW, dwIndex), copyInt },
738     { FIELD_OFFSET(MIB_IPADDRROW, dwMask), copyIpAddr },
739     { FIELD_OFFSET(MIB_IPADDRROW, dwBCastAddr), copyInt },
740     { FIELD_OFFSET(MIB_IPADDRROW, dwReasmSize), copyInt },
741 };
742
743 static void mib2IpAddrInit(void)
744 {
745     DWORD size = 0, ret = GetIpAddrTable(NULL, &size, TRUE);
746
747     if (ret == ERROR_INSUFFICIENT_BUFFER)
748     {
749         MIB_IPADDRTABLE *table = HeapAlloc(GetProcessHeap(), 0, size);
750         if (table)
751         {
752             if (!GetIpAddrTable(table, &size, TRUE)) ipAddrTable = table;
753             else HeapFree(GetProcessHeap(), 0, table );
754         }
755     }
756 }
757
758 static void mib2IpAddrCleanup(void)
759 {
760     HeapFree(GetProcessHeap(), 0, ipAddrTable);
761 }
762
763 static void oidToIpAddrRow(AsnObjectIdentifier *oid, void *dst)
764 {
765     MIB_IPADDRROW *row = dst;
766
767     row->dwAddr = oidToIpAddr(oid);
768 }
769
770 static int compareIpAddrRow(const void *a, const void *b)
771 {
772     const MIB_IPADDRROW *key = a, *value = b;
773
774     return key->dwAddr - value->dwAddr;
775 }
776
777 static BOOL mib2IpAddrQuery(BYTE bPduType, SnmpVarBind *pVarBind,
778     AsnInteger32 *pErrorStatus)
779 {
780     AsnObjectIdentifier myOid = DEFINE_OID(mib2IpAddr);
781     UINT tableIndex = 0, item = 0;
782     BOOL ret = TRUE;
783
784     TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name),
785         pErrorStatus);
786
787     switch (bPduType)
788     {
789     case SNMP_PDU_GET:
790     case SNMP_PDU_GETNEXT:
791         *pErrorStatus = getItemAndInstanceFromTable(&pVarBind->name,
792             &myOid, 4, bPduType, (struct GenericTable *)ipAddrTable,
793             sizeof(MIB_IPADDRROW), oidToIpAddrRow, compareIpAddrRow, &item,
794             &tableIndex);
795         if (!*pErrorStatus)
796         {
797             assert(tableIndex);
798             assert(item);
799             *pErrorStatus = mapStructEntryToValue(mib2IpAddrMap,
800                 DEFINE_SIZEOF(mib2IpAddrMap),
801                 &ipAddrTable->table[tableIndex - 1], item, bPduType, pVarBind);
802             if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT)
803                 ret = setOidWithItemAndIpAddr(&pVarBind->name, &myOid, item,
804                     ipAddrTable->table[tableIndex - 1].dwAddr);
805         }
806         break;
807     case SNMP_PDU_SET:
808         *pErrorStatus = SNMP_ERRORSTATUS_READONLY;
809         ret = FALSE;
810         break;
811     default:
812         FIXME("0x%02x: unsupported PDU type\n", bPduType);
813         *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
814     }
815     return ret;
816 }
817
818 static UINT mib2IpRoute[] = { 1,3,6,1,2,1,4,21,1 };
819 static PMIB_IPFORWARDTABLE ipRouteTable;
820
821 static struct structToAsnValue mib2IpRouteMap[] = {
822     { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardDest), copyIpAddr },
823     { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardIfIndex), copyInt },
824     { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardMetric1), copyInt },
825     { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardMetric2), copyInt },
826     { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardMetric3), copyInt },
827     { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardMetric4), copyInt },
828     { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardNextHop), copyIpAddr },
829     { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardType), copyInt },
830     { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardProto), copyInt },
831     { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardAge), copyInt },
832     { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardMask), copyIpAddr },
833     { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardMetric5), copyInt },
834 };
835
836 static void mib2IpRouteInit(void)
837 {
838     DWORD size = 0, ret = GetIpForwardTable(NULL, &size, TRUE);
839
840     if (ret == ERROR_INSUFFICIENT_BUFFER)
841     {
842         MIB_IPFORWARDTABLE *table = HeapAlloc(GetProcessHeap(), 0, size);
843         if (table)
844         {
845             if (!GetIpForwardTable(table, &size, TRUE)) ipRouteTable = table;
846             else HeapFree(GetProcessHeap(), 0, table );
847         }
848     }
849 }
850
851 static void mib2IpRouteCleanup(void)
852 {
853     HeapFree(GetProcessHeap(), 0, ipRouteTable);
854 }
855
856 static void oidToIpForwardRow(AsnObjectIdentifier *oid, void *dst)
857 {
858     MIB_IPFORWARDROW *row = dst;
859
860     row->dwForwardDest = oidToIpAddr(oid);
861 }
862
863 static int compareIpForwardRow(const void *a, const void *b)
864 {
865     const MIB_IPFORWARDROW *key = a, *value = b;
866
867     return key->dwForwardDest - value->dwForwardDest;
868 }
869
870 static BOOL mib2IpRouteQuery(BYTE bPduType, SnmpVarBind *pVarBind,
871     AsnInteger32 *pErrorStatus)
872 {
873     AsnObjectIdentifier myOid = DEFINE_OID(mib2IpRoute);
874     UINT tableIndex = 0, item = 0;
875     BOOL ret = TRUE;
876
877     TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name),
878         pErrorStatus);
879
880     switch (bPduType)
881     {
882     case SNMP_PDU_GET:
883     case SNMP_PDU_GETNEXT:
884         *pErrorStatus = getItemAndInstanceFromTable(&pVarBind->name,
885             &myOid, 4, bPduType, (struct GenericTable *)ipRouteTable,
886             sizeof(MIB_IPFORWARDROW), oidToIpForwardRow, compareIpForwardRow,
887             &item, &tableIndex);
888         if (!*pErrorStatus)
889         {
890             assert(tableIndex);
891             assert(item);
892             *pErrorStatus = mapStructEntryToValue(mib2IpRouteMap,
893                 DEFINE_SIZEOF(mib2IpRouteMap),
894                 &ipRouteTable->table[tableIndex - 1], item, bPduType, pVarBind);
895             if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT)
896                 ret = setOidWithItemAndIpAddr(&pVarBind->name, &myOid, item,
897                     ipRouteTable->table[tableIndex - 1].dwForwardDest);
898         }
899         break;
900     case SNMP_PDU_SET:
901         *pErrorStatus = SNMP_ERRORSTATUS_READONLY;
902         ret = FALSE;
903         break;
904     default:
905         FIXME("0x%02x: unsupported PDU type\n", bPduType);
906         *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
907     }
908     return ret;
909 }
910
911 static UINT mib2IpNet[] = { 1,3,6,1,2,1,4,22,1 };
912 static PMIB_IPNETTABLE ipNetTable;
913
914 static DWORD copyIpNetPhysAddr(AsnAny *value, void *src)
915 {
916     PMIB_IPNETROW row = (PMIB_IPNETROW)((BYTE *)src - FIELD_OFFSET(MIB_IPNETROW,
917                                         dwPhysAddrLen));
918
919     setStringValue(value, ASN_OCTETSTRING, row->dwPhysAddrLen, row->bPhysAddr);
920     return SNMP_ERRORSTATUS_NOERROR;
921 }
922
923 static struct structToAsnValue mib2IpNetMap[] = {
924     { FIELD_OFFSET(MIB_IPNETROW, dwIndex), copyInt },
925     { FIELD_OFFSET(MIB_IPNETROW, dwPhysAddrLen), copyIpNetPhysAddr },
926     { FIELD_OFFSET(MIB_IPNETROW, dwAddr), copyIpAddr },
927     { FIELD_OFFSET(MIB_IPNETROW, dwType), copyInt },
928 };
929
930 static void mib2IpNetInit(void)
931 {
932     DWORD size = 0, ret = GetIpNetTable(NULL, &size, FALSE);
933
934     if (ret == ERROR_INSUFFICIENT_BUFFER)
935     {
936         MIB_IPNETTABLE *table = HeapAlloc(GetProcessHeap(), 0, size);
937         if (table)
938         {
939             if (!GetIpNetTable(table, &size, FALSE)) ipNetTable = table;
940             else HeapFree(GetProcessHeap(), 0, table );
941         }
942     }
943 }
944
945 static void mib2IpNetCleanup(void)
946 {
947     HeapFree(GetProcessHeap(), 0, ipNetTable);
948 }
949
950 static BOOL mib2IpNetQuery(BYTE bPduType, SnmpVarBind *pVarBind,
951     AsnInteger32 *pErrorStatus)
952 {
953     AsnObjectIdentifier myOid = DEFINE_OID(mib2IpNet);
954     BOOL ret = TRUE;
955
956     TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name),
957         pErrorStatus);
958
959     switch (bPduType)
960     {
961     case SNMP_PDU_GET:
962     case SNMP_PDU_GETNEXT:
963         if (!ipNetTable)
964             *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
965         else
966         {
967             UINT tableIndex = 0, item = 0;
968
969             *pErrorStatus = getItemAndIntegerInstanceFromOid(&pVarBind->name,
970                 &myOid, bPduType, &item, &tableIndex);
971             if (!*pErrorStatus)
972             {
973                 assert(tableIndex);
974                 assert(item);
975                 if (tableIndex > ipNetTable->dwNumEntries)
976                     *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
977                 else
978                 {
979                     *pErrorStatus = mapStructEntryToValue(mib2IpNetMap,
980                         DEFINE_SIZEOF(mib2IpNetMap),
981                         &ipNetTable[tableIndex - 1], item, bPduType, pVarBind);
982                     if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT)
983                         ret = setOidWithItemAndInteger(&pVarBind->name, &myOid,
984                             item, tableIndex);
985                 }
986             }
987         }
988         break;
989     case SNMP_PDU_SET:
990         *pErrorStatus = SNMP_ERRORSTATUS_READONLY;
991         ret = FALSE;
992         break;
993     default:
994         FIXME("0x%02x: unsupported PDU type\n", bPduType);
995         *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
996     }
997     return ret;
998 }
999
1000 static UINT mib2Icmp[] = { 1,3,6,1,2,1,5 };
1001 static MIB_ICMP icmpStats;
1002
1003 static void mib2IcmpInit(void)
1004 {
1005     GetIcmpStatistics(&icmpStats);
1006 }
1007
1008 static struct structToAsnValue mib2IcmpMap[] = {
1009     { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwMsgs), copyInt },
1010     { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwErrors), copyInt },
1011     { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwDestUnreachs), copyInt },
1012     { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwTimeExcds), copyInt },
1013     { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwParmProbs), copyInt },
1014     { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwSrcQuenchs), copyInt },
1015     { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwRedirects), copyInt },
1016     { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwEchos), copyInt },
1017     { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwEchoReps), copyInt },
1018     { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwTimestamps), copyInt },
1019     { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwTimestampReps), copyInt },
1020     { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwAddrMasks), copyInt },
1021     { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwAddrMaskReps), copyInt },
1022     { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwMsgs), copyInt },
1023     { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwErrors), copyInt },
1024     { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwDestUnreachs), copyInt },
1025     { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwTimeExcds), copyInt },
1026     { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwParmProbs), copyInt },
1027     { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwSrcQuenchs), copyInt },
1028     { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwRedirects), copyInt },
1029     { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwEchos), copyInt },
1030     { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwEchoReps), copyInt },
1031     { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwTimestamps), copyInt },
1032     { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwTimestampReps), copyInt },
1033     { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwAddrMasks), copyInt },
1034     { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwAddrMaskReps), copyInt },
1035 };
1036
1037 static BOOL mib2IcmpQuery(BYTE bPduType, SnmpVarBind *pVarBind,
1038     AsnInteger32 *pErrorStatus)
1039 {
1040     AsnObjectIdentifier myOid = DEFINE_OID(mib2Icmp);
1041     UINT item = 0;
1042     BOOL ret = TRUE;
1043
1044     TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name),
1045         pErrorStatus);
1046
1047     switch (bPduType)
1048     {
1049     case SNMP_PDU_GET:
1050     case SNMP_PDU_GETNEXT:
1051         *pErrorStatus = getItemFromOid(&pVarBind->name, &myOid, bPduType,
1052             &item);
1053         if (!*pErrorStatus)
1054         {
1055             *pErrorStatus = mapStructEntryToValue(mib2IcmpMap,
1056                 DEFINE_SIZEOF(mib2IcmpMap), &icmpStats, item, bPduType,
1057                 pVarBind);
1058             if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT)
1059                 ret = setOidWithItem(&pVarBind->name, &myOid, item);
1060         }
1061         break;
1062     case SNMP_PDU_SET:
1063         *pErrorStatus = SNMP_ERRORSTATUS_READONLY;
1064         ret = FALSE;
1065         break;
1066     default:
1067         FIXME("0x%02x: unsupported PDU type\n", bPduType);
1068         *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
1069     }
1070     return ret;
1071 }
1072
1073 static UINT mib2Tcp[] = { 1,3,6,1,2,1,6 };
1074 static MIB_TCPSTATS tcpStats;
1075
1076 static void mib2TcpInit(void)
1077 {
1078     GetTcpStatistics(&tcpStats);
1079 }
1080
1081 static struct structToAsnValue mib2TcpMap[] = {
1082     { FIELD_OFFSET(MIB_TCPSTATS, dwRtoAlgorithm), copyInt },
1083     { FIELD_OFFSET(MIB_TCPSTATS, dwRtoMin), copyInt },
1084     { FIELD_OFFSET(MIB_TCPSTATS, dwRtoMax), copyInt },
1085     { FIELD_OFFSET(MIB_TCPSTATS, dwMaxConn), copyInt },
1086     { FIELD_OFFSET(MIB_TCPSTATS, dwActiveOpens), copyInt },
1087     { FIELD_OFFSET(MIB_TCPSTATS, dwPassiveOpens), copyInt },
1088     { FIELD_OFFSET(MIB_TCPSTATS, dwAttemptFails), copyInt },
1089     { FIELD_OFFSET(MIB_TCPSTATS, dwEstabResets), copyInt },
1090     { FIELD_OFFSET(MIB_TCPSTATS, dwCurrEstab), copyInt },
1091     { FIELD_OFFSET(MIB_TCPSTATS, dwInSegs), copyInt },
1092     { FIELD_OFFSET(MIB_TCPSTATS, dwOutSegs), copyInt },
1093     { FIELD_OFFSET(MIB_TCPSTATS, dwRetransSegs), copyInt },
1094     { FIELD_OFFSET(MIB_TCPSTATS, dwInErrs), copyInt },
1095     { FIELD_OFFSET(MIB_TCPSTATS, dwOutRsts), copyInt },
1096     { FIELD_OFFSET(MIB_TCPSTATS, dwNumConns), copyInt },
1097 };
1098
1099 static BOOL mib2TcpQuery(BYTE bPduType, SnmpVarBind *pVarBind,
1100     AsnInteger32 *pErrorStatus)
1101 {
1102     AsnObjectIdentifier myOid = DEFINE_OID(mib2Tcp);
1103     UINT item = 0;
1104     BOOL ret = TRUE;
1105
1106     TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name),
1107         pErrorStatus);
1108
1109     switch (bPduType)
1110     {
1111     case SNMP_PDU_GET:
1112     case SNMP_PDU_GETNEXT:
1113         *pErrorStatus = getItemFromOid(&pVarBind->name, &myOid, bPduType,
1114             &item);
1115         if (!*pErrorStatus)
1116         {
1117             *pErrorStatus = mapStructEntryToValue(mib2TcpMap,
1118                 DEFINE_SIZEOF(mib2TcpMap), &tcpStats, item, bPduType, pVarBind);
1119             if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT)
1120                 ret = setOidWithItem(&pVarBind->name, &myOid, item);
1121         }
1122         break;
1123     case SNMP_PDU_SET:
1124         *pErrorStatus = SNMP_ERRORSTATUS_READONLY;
1125         ret = FALSE;
1126         break;
1127     default:
1128         FIXME("0x%02x: unsupported PDU type\n", bPduType);
1129         *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
1130     }
1131     return ret;
1132 }
1133
1134 static UINT mib2Udp[] = { 1,3,6,1,2,1,7 };
1135 static MIB_UDPSTATS udpStats;
1136
1137 static void mib2UdpInit(void)
1138 {
1139     GetUdpStatistics(&udpStats);
1140 }
1141
1142 static struct structToAsnValue mib2UdpMap[] = {
1143     { FIELD_OFFSET(MIB_UDPSTATS, dwInDatagrams), copyInt },
1144     { FIELD_OFFSET(MIB_UDPSTATS, dwNoPorts), copyInt },
1145     { FIELD_OFFSET(MIB_UDPSTATS, dwInErrors), copyInt },
1146     { FIELD_OFFSET(MIB_UDPSTATS, dwOutDatagrams), copyInt },
1147 };
1148
1149 static BOOL mib2UdpQuery(BYTE bPduType, SnmpVarBind *pVarBind,
1150     AsnInteger32 *pErrorStatus)
1151 {
1152     AsnObjectIdentifier myOid = DEFINE_OID(mib2Udp);
1153     UINT item;
1154     BOOL ret = TRUE;
1155
1156     TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name),
1157         pErrorStatus);
1158
1159     switch (bPduType)
1160     {
1161     case SNMP_PDU_GET:
1162     case SNMP_PDU_GETNEXT:
1163         *pErrorStatus = getItemFromOid(&pVarBind->name, &myOid, bPduType,
1164             &item);
1165         if (!*pErrorStatus)
1166         {
1167             *pErrorStatus = mapStructEntryToValue(mib2UdpMap,
1168                 DEFINE_SIZEOF(mib2UdpMap), &udpStats, item, bPduType, pVarBind);
1169             if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT)
1170                 ret = setOidWithItem(&pVarBind->name, &myOid, item);
1171         }
1172         break;
1173     case SNMP_PDU_SET:
1174         *pErrorStatus = SNMP_ERRORSTATUS_READONLY;
1175         ret = FALSE;
1176         break;
1177     default:
1178         FIXME("0x%02x: unsupported PDU type\n", bPduType);
1179         *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
1180     }
1181     return ret;
1182 }
1183
1184 static UINT mib2UdpEntry[] = { 1,3,6,1,2,1,7,5,1 };
1185 static PMIB_UDPTABLE udpTable;
1186
1187 static void mib2UdpEntryInit(void)
1188 {
1189     DWORD size = 0, ret = GetUdpTable(NULL, &size, TRUE);
1190
1191     if (ret == ERROR_INSUFFICIENT_BUFFER)
1192     {
1193         MIB_UDPTABLE *table = HeapAlloc(GetProcessHeap(), 0, size);
1194         if (table)
1195         {
1196             if (!GetUdpTable(table, &size, TRUE)) udpTable = table;
1197             else HeapFree(GetProcessHeap(), 0, table);
1198         }
1199     }
1200 }
1201
1202 static void mib2UdpEntryCleanup(void)
1203 {
1204     HeapFree(GetProcessHeap(), 0, udpTable);
1205 }
1206
1207 static struct structToAsnValue mib2UdpEntryMap[] = {
1208     { FIELD_OFFSET(MIB_UDPROW, dwLocalAddr), copyIpAddr },
1209     { FIELD_OFFSET(MIB_UDPROW, dwLocalPort), copyInt },
1210 };
1211
1212 static void oidToUdpRow(AsnObjectIdentifier *oid, void *dst)
1213 {
1214     MIB_UDPROW *row = dst;
1215
1216     assert(oid && oid->idLength >= 5);
1217     row->dwLocalAddr = oidToIpAddr(oid);
1218     row->dwLocalPort = oid->ids[4];
1219 }
1220
1221 static int compareUdpRow(const void *a, const void *b)
1222 {
1223     const MIB_UDPROW *key = a, *value = b;
1224     int ret;
1225
1226     ret = key->dwLocalAddr - value->dwLocalAddr;
1227     if (ret == 0)
1228         ret = key->dwLocalPort - value->dwLocalPort;
1229     return ret;
1230 }
1231
1232 static BOOL mib2UdpEntryQuery(BYTE bPduType, SnmpVarBind *pVarBind,
1233     AsnInteger32 *pErrorStatus)
1234 {
1235     AsnObjectIdentifier myOid = DEFINE_OID(mib2UdpEntry);
1236     BOOL ret = TRUE;
1237
1238     TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name),
1239         pErrorStatus);
1240
1241     switch (bPduType)
1242     {
1243     case SNMP_PDU_GET:
1244     case SNMP_PDU_GETNEXT:
1245         if (!udpTable)
1246             *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
1247         else
1248         {
1249             UINT tableIndex = 0, item = 0;
1250
1251             *pErrorStatus = getItemAndInstanceFromTable(&pVarBind->name, &myOid,
1252                 5, bPduType, (struct GenericTable *)udpTable,
1253                 sizeof(MIB_UDPROW), oidToUdpRow, compareUdpRow, &item,
1254                 &tableIndex);
1255             if (!*pErrorStatus)
1256             {
1257                 assert(tableIndex);
1258                 assert(item);
1259                 *pErrorStatus = mapStructEntryToValue(mib2UdpEntryMap,
1260                     DEFINE_SIZEOF(mib2UdpEntryMap),
1261                     &udpTable->table[tableIndex - 1], item, bPduType, pVarBind);
1262                 if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT)
1263                 {
1264                     AsnObjectIdentifier oid;
1265
1266                     ret = setOidWithItemAndIpAddr(&pVarBind->name, &myOid, item,
1267                         udpTable->table[tableIndex - 1].dwLocalAddr);
1268                     if (ret)
1269                     {
1270                         oid.idLength = 1;
1271                         oid.ids = &udpTable->table[tableIndex - 1].dwLocalPort;
1272                         ret = SnmpUtilOidAppend(&pVarBind->name, &oid);
1273                     }
1274                 }
1275             }
1276         }
1277         break;
1278     case SNMP_PDU_SET:
1279         *pErrorStatus = SNMP_ERRORSTATUS_READONLY;
1280         ret = FALSE;
1281         break;
1282     default:
1283         FIXME("0x%02x: unsupported PDU type\n", bPduType);
1284         *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
1285     }
1286     return ret;
1287 }
1288
1289 /* This list MUST BE lexicographically sorted */
1290 static struct mibImplementation supportedIDs[] = {
1291     { DEFINE_OID(mib2IfNumber), mib2IfNumberInit, mib2IfNumberQuery,
1292       mib2IfNumberCleanup },
1293     { DEFINE_OID(mib2IfEntry), NULL, mib2IfEntryQuery, NULL },
1294     { DEFINE_OID(mib2Ip), mib2IpStatsInit, mib2IpStatsQuery, NULL },
1295     { DEFINE_OID(mib2IpAddr), mib2IpAddrInit, mib2IpAddrQuery,
1296       mib2IpAddrCleanup },
1297     { DEFINE_OID(mib2IpRoute), mib2IpRouteInit, mib2IpRouteQuery,
1298       mib2IpRouteCleanup },
1299     { DEFINE_OID(mib2IpNet), mib2IpNetInit, mib2IpNetQuery, mib2IpNetCleanup },
1300     { DEFINE_OID(mib2Icmp), mib2IcmpInit, mib2IcmpQuery, NULL },
1301     { DEFINE_OID(mib2Tcp), mib2TcpInit, mib2TcpQuery, NULL },
1302     { DEFINE_OID(mib2Udp), mib2UdpInit, mib2UdpQuery, NULL },
1303     { DEFINE_OID(mib2UdpEntry), mib2UdpEntryInit, mib2UdpEntryQuery,
1304       mib2UdpEntryCleanup },
1305 };
1306 static UINT minSupportedIDLength;
1307
1308 /*****************************************************************************
1309  * SnmpExtensionInit [INETMIB1.@]
1310  */
1311 BOOL WINAPI SnmpExtensionInit(DWORD dwUptimeReference,
1312     HANDLE *phSubagentTrapEvent, AsnObjectIdentifier *pFirstSupportedRegion)
1313 {
1314     AsnObjectIdentifier myOid = DEFINE_OID(mib2System);
1315     UINT i;
1316
1317     TRACE("(%d, %p, %p)\n", dwUptimeReference, phSubagentTrapEvent,
1318         pFirstSupportedRegion);
1319
1320     minSupportedIDLength = UINT_MAX;
1321     for (i = 0; i < sizeof(supportedIDs) / sizeof(supportedIDs[0]); i++)
1322     {
1323         if (supportedIDs[i].init)
1324             supportedIDs[i].init();
1325         if (supportedIDs[i].name.idLength < minSupportedIDLength)
1326             minSupportedIDLength = supportedIDs[i].name.idLength;
1327     }
1328     *phSubagentTrapEvent = NULL;
1329     SnmpUtilOidCpy(pFirstSupportedRegion, &myOid);
1330     return TRUE;
1331 }
1332
1333 static void cleanup(void)
1334 {
1335     UINT i;
1336
1337     for (i = 0; i < sizeof(supportedIDs) / sizeof(supportedIDs[0]); i++)
1338         if (supportedIDs[i].cleanup)
1339             supportedIDs[i].cleanup();
1340 }
1341
1342 static struct mibImplementation *findSupportedQuery(UINT *ids, UINT idLength,
1343     UINT *matchingIndex)
1344 {
1345     int indexHigh = DEFINE_SIZEOF(supportedIDs) - 1, indexLow = 0, i;
1346     struct mibImplementation *impl = NULL;
1347     AsnObjectIdentifier oid1 = { idLength, ids};
1348
1349     if (!idLength)
1350         return NULL;
1351     for (i = (indexLow + indexHigh) / 2; !impl && indexLow <= indexHigh;
1352          i = (indexLow + indexHigh) / 2)
1353     {
1354         INT cmp;
1355
1356         cmp = SnmpUtilOidNCmp(&oid1, &supportedIDs[i].name, idLength);
1357         if (!cmp)
1358         {
1359             impl = &supportedIDs[i];
1360             *matchingIndex = i;
1361         }
1362         else if (cmp > 0)
1363             indexLow = i + 1;
1364         else
1365             indexHigh = i - 1;
1366     }
1367     return impl;
1368 }
1369
1370 /*****************************************************************************
1371  * SnmpExtensionQuery [INETMIB1.@]
1372  */
1373 BOOL WINAPI SnmpExtensionQuery(BYTE bPduType, SnmpVarBindList *pVarBindList,
1374     AsnInteger32 *pErrorStatus, AsnInteger32 *pErrorIndex)
1375 {
1376     AsnObjectIdentifier mib2oid = DEFINE_OID(mib2);
1377     AsnInteger32 error = SNMP_ERRORSTATUS_NOERROR, errorIndex = 0;
1378     UINT i;
1379     BOOL ret = TRUE;
1380
1381     TRACE("(0x%02x, %p, %p, %p)\n", bPduType, pVarBindList,
1382         pErrorStatus, pErrorIndex);
1383
1384     for (i = 0; !error && i < pVarBindList->len; i++)
1385     {
1386         /* Ignore any OIDs not in MIB2 */
1387         if (!SnmpUtilOidNCmp(&pVarBindList->list[i].name, &mib2oid,
1388             mib2oid.idLength))
1389         {
1390             struct mibImplementation *impl = NULL;
1391             UINT len, matchingIndex = 0;
1392
1393             TRACE("%s\n", SnmpUtilOidToA(&pVarBindList->list[i].name));
1394             /* Search for an implementation matching as many octets as possible
1395              */
1396             for (len = pVarBindList->list[i].name.idLength;
1397                 len >= minSupportedIDLength && !impl; len--)
1398                 impl = findSupportedQuery(pVarBindList->list[i].name.ids, len,
1399                     &matchingIndex);
1400             if (impl && impl->query)
1401                 ret = impl->query(bPduType, &pVarBindList->list[i], &error);
1402             else
1403                 error = SNMP_ERRORSTATUS_NOSUCHNAME;
1404             if (error == SNMP_ERRORSTATUS_NOSUCHNAME &&
1405                 bPduType == SNMP_PDU_GETNEXT)
1406             {
1407                 /* GetNext is special: it finds the successor to the given OID,
1408                  * so we have to continue until an implementation handles the
1409                  * query or we exhaust the table of supported OIDs.
1410                  */
1411                 for (matchingIndex++; error == SNMP_ERRORSTATUS_NOSUCHNAME &&
1412                     matchingIndex < DEFINE_SIZEOF(supportedIDs);
1413                     matchingIndex++)
1414                 {
1415                     error = SNMP_ERRORSTATUS_NOERROR;
1416                     impl = &supportedIDs[matchingIndex];
1417                     if (impl->query)
1418                         ret = impl->query(bPduType, &pVarBindList->list[i],
1419                             &error);
1420                     else
1421                         error = SNMP_ERRORSTATUS_NOSUCHNAME;
1422                 }
1423                 /* If the query still isn't resolved, set the OID to the
1424                  * successor to the last entry in the table.
1425                  */
1426                 if (error == SNMP_ERRORSTATUS_NOSUCHNAME)
1427                 {
1428                     SnmpUtilOidFree(&pVarBindList->list[i].name);
1429                     ret = SnmpUtilOidCpy(&pVarBindList->list[i].name,
1430                         &supportedIDs[matchingIndex - 1].name);
1431                     pVarBindList->list[i].name.ids[
1432                         pVarBindList->list[i].name.idLength - 1] += 1;
1433                 }
1434             }
1435             if (error)
1436                 errorIndex = i + 1;
1437         }
1438     }
1439     *pErrorStatus = error;
1440     *pErrorIndex = errorIndex;
1441     return ret;
1442 }
1443
1444 /*****************************************************************************
1445  * DllMain [INETMIB1.@]
1446  */
1447 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
1448 {
1449     TRACE("(0x%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved);
1450
1451     switch (fdwReason)
1452     {
1453         case DLL_PROCESS_ATTACH:
1454             DisableThreadLibraryCalls(hinstDLL);
1455             break;
1456         case DLL_PROCESS_DETACH:
1457             cleanup();
1458             break;
1459         default:
1460             break;
1461     }
1462
1463     return TRUE;
1464 }