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