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