ole32: Use existing MTA in CoGetContextToken and CoGetObjectContext.
[wine] / dlls / inetmib1 / main.c
1 /*
2  * Copyright 2008 Juan Lang
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include "config.h"
20 #include <assert.h>
21 #include <stdarg.h>
22 #include <stdlib.h>
23 #include <limits.h>
24 #include "windef.h"
25 #include "winbase.h"
26 #include "snmp.h"
27 #include "iphlpapi.h"
28 #include "wine/debug.h"
29
30 WINE_DEFAULT_DEBUG_CHANNEL(inetmib1);
31
32 /**
33  * Utility functions
34  */
35 static void copyInt(AsnAny *value, void *src)
36 {
37     value->asnType = ASN_INTEGER;
38     value->asnValue.number = *(DWORD *)src;
39 }
40
41 static void setStringValue(AsnAny *value, BYTE type, DWORD len, BYTE *str)
42 {
43     AsnAny strValue;
44
45     strValue.asnType = type;
46     strValue.asnValue.string.stream = str;
47     strValue.asnValue.string.length = len;
48     strValue.asnValue.string.dynamic = TRUE;
49     SnmpUtilAsnAnyCpy(value, &strValue);
50 }
51
52 static void copyLengthPrecededString(AsnAny *value, void *src)
53 {
54     DWORD len = *(DWORD *)src;
55
56     setStringValue(value, ASN_OCTETSTRING, len, (BYTE *)src + sizeof(DWORD));
57 }
58
59 typedef void (*copyValueFunc)(AsnAny *value, void *src);
60
61 struct structToAsnValue
62 {
63     size_t        offset;
64     copyValueFunc copy;
65 };
66
67 static AsnInteger32 mapStructEntryToValue(struct structToAsnValue *map,
68     UINT mapLen, void *record, UINT id, BYTE bPduType, SnmpVarBind *pVarBind)
69 {
70     /* OIDs are 1-based */
71     if (!id)
72         return SNMP_ERRORSTATUS_NOSUCHNAME;
73     --id;
74     if (id >= mapLen)
75         return SNMP_ERRORSTATUS_NOSUCHNAME;
76     if (!map[id].copy)
77         return SNMP_ERRORSTATUS_NOSUCHNAME;
78     map[id].copy(&pVarBind->value, (BYTE *)record + map[id].offset);
79     return SNMP_ERRORSTATUS_NOERROR;
80 }
81
82 static void copyIpAddr(AsnAny *value, void *src)
83 {
84     setStringValue(value, ASN_IPADDRESS, sizeof(DWORD), src);
85 }
86
87 static UINT mib2[] = { 1,3,6,1,2,1 };
88 static UINT mib2System[] = { 1,3,6,1,2,1,1 };
89
90 typedef BOOL (*varqueryfunc)(BYTE bPduType, SnmpVarBind *pVarBind,
91     AsnInteger32 *pErrorStatus);
92
93 struct mibImplementation
94 {
95     AsnObjectIdentifier name;
96     void              (*init)(void);
97     varqueryfunc        query;
98     void              (*cleanup)(void);
99 };
100
101 static UINT mib2IfNumber[] = { 1,3,6,1,2,1,2,1 };
102 static PMIB_IFTABLE ifTable;
103
104 static void mib2IfNumberInit(void)
105 {
106     DWORD size = 0, ret = GetIfTable(NULL, &size, FALSE);
107
108     if (ret == ERROR_INSUFFICIENT_BUFFER)
109     {
110         MIB_IFTABLE *table = HeapAlloc(GetProcessHeap(), 0, size);
111         if (table)
112         {
113             if (!GetIfTable(table, &size, FALSE)) ifTable = table;
114             else HeapFree(GetProcessHeap(), 0, table );
115         }
116     }
117 }
118
119 static void mib2IfNumberCleanup(void)
120 {
121     HeapFree(GetProcessHeap(), 0, ifTable);
122 }
123
124 static BOOL mib2IfNumberQuery(BYTE bPduType, SnmpVarBind *pVarBind,
125     AsnInteger32 *pErrorStatus)
126 {
127     AsnObjectIdentifier numberOid = DEFINE_OID(mib2IfNumber);
128     BOOL ret = TRUE;
129
130     TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name),
131         pErrorStatus);
132
133     switch (bPduType)
134     {
135     case SNMP_PDU_GET:
136     case SNMP_PDU_GETNEXT:
137         if ((bPduType == SNMP_PDU_GET &&
138             !SnmpUtilOidNCmp(&pVarBind->name, &numberOid, numberOid.idLength))
139             || SnmpUtilOidNCmp(&pVarBind->name, &numberOid, numberOid.idLength)
140             < 0)
141         {
142             DWORD numIfs = ifTable ? ifTable->dwNumEntries : 0;
143
144             copyInt(&pVarBind->value, &numIfs);
145             if (bPduType == SNMP_PDU_GETNEXT)
146             {
147                 SnmpUtilOidFree(&pVarBind->name);
148                 SnmpUtilOidCpy(&pVarBind->name, &numberOid);
149             }
150             *pErrorStatus = SNMP_ERRORSTATUS_NOERROR;
151         }
152         else
153         {
154             *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
155             /* Caller deals with OID if bPduType == SNMP_PDU_GETNEXT, so don't
156              * need to set it here.
157              */
158         }
159         break;
160     case SNMP_PDU_SET:
161         *pErrorStatus = SNMP_ERRORSTATUS_READONLY;
162         ret = FALSE;
163         break;
164     default:
165         FIXME("0x%02x: unsupported PDU type\n", bPduType);
166         *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
167     }
168     return ret;
169 }
170
171 static void copyOperStatus(AsnAny *value, void *src)
172 {
173     value->asnType = ASN_INTEGER;
174     /* The IPHlpApi definition of operational status differs from the MIB2 one,
175      * so map it to the MIB2 value.
176      */
177     switch (*(DWORD *)src)
178     {
179     case MIB_IF_OPER_STATUS_OPERATIONAL:
180         value->asnValue.number = MIB_IF_ADMIN_STATUS_UP;
181         break;
182     case MIB_IF_OPER_STATUS_CONNECTING:
183     case MIB_IF_OPER_STATUS_CONNECTED:
184         value->asnValue.number = MIB_IF_ADMIN_STATUS_TESTING;
185         break;
186     default:
187         value->asnValue.number = MIB_IF_ADMIN_STATUS_DOWN;
188     };
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]) == 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 struct structToAsnValue mib2IfEntryMap[] = {
554     { FIELD_OFFSET(MIB_IFROW, dwIndex), copyInt },
555     { FIELD_OFFSET(MIB_IFROW, dwDescrLen), copyLengthPrecededString },
556     { FIELD_OFFSET(MIB_IFROW, dwType), copyInt },
557     { FIELD_OFFSET(MIB_IFROW, dwMtu), copyInt },
558     { FIELD_OFFSET(MIB_IFROW, dwSpeed), copyInt },
559     { FIELD_OFFSET(MIB_IFROW, dwPhysAddrLen), copyLengthPrecededString },
560     { FIELD_OFFSET(MIB_IFROW, dwAdminStatus), copyInt },
561     { FIELD_OFFSET(MIB_IFROW, dwOperStatus), copyOperStatus },
562     { FIELD_OFFSET(MIB_IFROW, dwLastChange), copyInt },
563     { FIELD_OFFSET(MIB_IFROW, dwInOctets), copyInt },
564     { FIELD_OFFSET(MIB_IFROW, dwInUcastPkts), copyInt },
565     { FIELD_OFFSET(MIB_IFROW, dwInNUcastPkts), copyInt },
566     { FIELD_OFFSET(MIB_IFROW, dwInDiscards), copyInt },
567     { FIELD_OFFSET(MIB_IFROW, dwInErrors), copyInt },
568     { FIELD_OFFSET(MIB_IFROW, dwInUnknownProtos), copyInt },
569     { FIELD_OFFSET(MIB_IFROW, dwOutOctets), copyInt },
570     { FIELD_OFFSET(MIB_IFROW, dwOutUcastPkts), copyInt },
571     { FIELD_OFFSET(MIB_IFROW, dwOutNUcastPkts), copyInt },
572     { FIELD_OFFSET(MIB_IFROW, dwOutDiscards), copyInt },
573     { FIELD_OFFSET(MIB_IFROW, dwOutErrors), copyInt },
574     { FIELD_OFFSET(MIB_IFROW, dwOutQLen), copyInt },
575 };
576
577 static UINT mib2IfEntry[] = { 1,3,6,1,2,1,2,2,1 };
578
579 static BOOL mib2IfEntryQuery(BYTE bPduType, SnmpVarBind *pVarBind,
580     AsnInteger32 *pErrorStatus)
581 {
582     AsnObjectIdentifier entryOid = DEFINE_OID(mib2IfEntry);
583     BOOL ret = TRUE;
584
585     TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name),
586         pErrorStatus);
587
588     switch (bPduType)
589     {
590     case SNMP_PDU_GET:
591     case SNMP_PDU_GETNEXT:
592         if (!ifTable)
593         {
594             /* There is no interface present, so let the caller deal
595              * with finding the successor.
596              */
597             *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
598         }
599         else
600         {
601             UINT tableIndex = 0, item = 0;
602
603             *pErrorStatus = getItemAndIntegerInstanceFromOid(&pVarBind->name,
604                 &entryOid, bPduType, &item, &tableIndex);
605             if (!*pErrorStatus)
606             {
607                 assert(tableIndex);
608                 assert(item);
609                 if (tableIndex > ifTable->dwNumEntries)
610                     *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
611                 else
612                 {
613                     *pErrorStatus = mapStructEntryToValue(mib2IfEntryMap,
614                         DEFINE_SIZEOF(mib2IfEntryMap),
615                         &ifTable->table[tableIndex - 1], item, bPduType,
616                         pVarBind);
617                     if (bPduType == SNMP_PDU_GETNEXT)
618                         ret = setOidWithItemAndInteger(&pVarBind->name,
619                             &entryOid, item, tableIndex);
620                 }
621             }
622         }
623         break;
624     case SNMP_PDU_SET:
625         *pErrorStatus = SNMP_ERRORSTATUS_READONLY;
626         ret = FALSE;
627         break;
628     default:
629         FIXME("0x%02x: unsupported PDU type\n", bPduType);
630         *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
631     }
632     return ret;
633 }
634
635 static UINT mib2Ip[] = { 1,3,6,1,2,1,4 };
636 static MIB_IPSTATS ipStats;
637
638 static void mib2IpStatsInit(void)
639 {
640     GetIpStatistics(&ipStats);
641 }
642
643 static struct structToAsnValue mib2IpMap[] = {
644     { FIELD_OFFSET(MIB_IPSTATS, dwForwarding), copyInt }, /* 1 */
645     { FIELD_OFFSET(MIB_IPSTATS, dwDefaultTTL), copyInt }, /* 2 */
646     { FIELD_OFFSET(MIB_IPSTATS, dwInReceives), copyInt }, /* 3 */
647     { FIELD_OFFSET(MIB_IPSTATS, dwInHdrErrors), copyInt }, /* 4 */
648     { FIELD_OFFSET(MIB_IPSTATS, dwInAddrErrors), copyInt }, /* 5 */
649     { FIELD_OFFSET(MIB_IPSTATS, dwForwDatagrams), copyInt }, /* 6 */
650     { FIELD_OFFSET(MIB_IPSTATS, dwInUnknownProtos), copyInt }, /* 7 */
651     { FIELD_OFFSET(MIB_IPSTATS, dwInDiscards), copyInt }, /* 8 */
652     { FIELD_OFFSET(MIB_IPSTATS, dwInDelivers), copyInt }, /* 9 */
653     { FIELD_OFFSET(MIB_IPSTATS, dwOutRequests), copyInt }, /* 10 */
654     { FIELD_OFFSET(MIB_IPSTATS, dwOutDiscards), copyInt }, /* 11 */
655     { FIELD_OFFSET(MIB_IPSTATS, dwOutNoRoutes), copyInt }, /* 12 */
656     { FIELD_OFFSET(MIB_IPSTATS, dwReasmTimeout), copyInt }, /* 13 */
657     { FIELD_OFFSET(MIB_IPSTATS, dwReasmReqds), copyInt }, /* 14 */
658     { FIELD_OFFSET(MIB_IPSTATS, dwReasmOks), copyInt }, /* 15 */
659     { FIELD_OFFSET(MIB_IPSTATS, dwReasmFails), copyInt }, /* 16 */
660     { FIELD_OFFSET(MIB_IPSTATS, dwFragOks), copyInt }, /* 17 */
661     { FIELD_OFFSET(MIB_IPSTATS, dwFragFails), copyInt }, /* 18 */
662     { FIELD_OFFSET(MIB_IPSTATS, dwFragCreates), copyInt }, /* 19 */
663     { 0, NULL }, /* 20: not used, IP addr table */
664     { 0, NULL }, /* 21: not used, route table */
665     { 0, NULL }, /* 22: not used, net to media (ARP) table */
666     { FIELD_OFFSET(MIB_IPSTATS, dwRoutingDiscards), copyInt }, /* 23 */
667 };
668
669 static BOOL mib2IpStatsQuery(BYTE bPduType, SnmpVarBind *pVarBind,
670     AsnInteger32 *pErrorStatus)
671 {
672     AsnObjectIdentifier myOid = DEFINE_OID(mib2Ip);
673     UINT item = 0;
674     BOOL ret = TRUE;
675
676     TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name),
677         pErrorStatus);
678
679     switch (bPduType)
680     {
681     case SNMP_PDU_GET:
682     case SNMP_PDU_GETNEXT:
683         *pErrorStatus = getItemFromOid(&pVarBind->name, &myOid, bPduType,
684             &item);
685         if (!*pErrorStatus)
686         {
687             *pErrorStatus = mapStructEntryToValue(mib2IpMap,
688                 DEFINE_SIZEOF(mib2IpMap), &ipStats, item, bPduType, pVarBind);
689             if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT)
690                 ret = setOidWithItem(&pVarBind->name, &myOid, item);
691         }
692         break;
693     case SNMP_PDU_SET:
694         *pErrorStatus = SNMP_ERRORSTATUS_READONLY;
695         ret = FALSE;
696         break;
697     default:
698         FIXME("0x%02x: unsupported PDU type\n", bPduType);
699         *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
700     }
701     return ret;
702 }
703
704 static UINT mib2IpAddr[] = { 1,3,6,1,2,1,4,20,1 };
705 static PMIB_IPADDRTABLE ipAddrTable;
706
707 static struct structToAsnValue mib2IpAddrMap[] = {
708     { FIELD_OFFSET(MIB_IPADDRROW, dwAddr), copyIpAddr },
709     { FIELD_OFFSET(MIB_IPADDRROW, dwIndex), copyInt },
710     { FIELD_OFFSET(MIB_IPADDRROW, dwMask), copyIpAddr },
711     { FIELD_OFFSET(MIB_IPADDRROW, dwBCastAddr), copyInt },
712     { FIELD_OFFSET(MIB_IPADDRROW, dwReasmSize), copyInt },
713 };
714
715 static void mib2IpAddrInit(void)
716 {
717     DWORD size = 0, ret = GetIpAddrTable(NULL, &size, TRUE);
718
719     if (ret == ERROR_INSUFFICIENT_BUFFER)
720     {
721         MIB_IPADDRTABLE *table = HeapAlloc(GetProcessHeap(), 0, size);
722         if (table)
723         {
724             if (!GetIpAddrTable(table, &size, TRUE)) ipAddrTable = table;
725             else HeapFree(GetProcessHeap(), 0, table );
726         }
727     }
728 }
729
730 static void mib2IpAddrCleanup(void)
731 {
732     HeapFree(GetProcessHeap(), 0, ipAddrTable);
733 }
734
735 static void oidToIpAddrRow(AsnObjectIdentifier *oid, void *dst)
736 {
737     MIB_IPADDRROW *row = dst;
738
739     row->dwAddr = oidToIpAddr(oid);
740 }
741
742 static int compareIpAddrRow(const void *a, const void *b)
743 {
744     const MIB_IPADDRROW *key = a, *value = b;
745
746     return key->dwAddr - value->dwAddr;
747 }
748
749 static BOOL mib2IpAddrQuery(BYTE bPduType, SnmpVarBind *pVarBind,
750     AsnInteger32 *pErrorStatus)
751 {
752     AsnObjectIdentifier myOid = DEFINE_OID(mib2IpAddr);
753     UINT tableIndex = 0, item = 0;
754     BOOL ret = TRUE;
755
756     TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name),
757         pErrorStatus);
758
759     switch (bPduType)
760     {
761     case SNMP_PDU_GET:
762     case SNMP_PDU_GETNEXT:
763         *pErrorStatus = getItemAndInstanceFromTable(&pVarBind->name,
764             &myOid, 4, bPduType, (struct GenericTable *)ipAddrTable,
765             sizeof(MIB_IPADDRROW), oidToIpAddrRow, compareIpAddrRow, &item,
766             &tableIndex);
767         if (!*pErrorStatus)
768         {
769             assert(tableIndex);
770             assert(item);
771             *pErrorStatus = mapStructEntryToValue(mib2IpAddrMap,
772                 DEFINE_SIZEOF(mib2IpAddrMap),
773                 &ipAddrTable->table[tableIndex - 1], item, bPduType, pVarBind);
774             if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT)
775                 ret = setOidWithItemAndIpAddr(&pVarBind->name, &myOid, item,
776                     ipAddrTable->table[tableIndex - 1].dwAddr);
777         }
778         break;
779     case SNMP_PDU_SET:
780         *pErrorStatus = SNMP_ERRORSTATUS_READONLY;
781         ret = FALSE;
782         break;
783     default:
784         FIXME("0x%02x: unsupported PDU type\n", bPduType);
785         *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
786     }
787     return ret;
788 }
789
790 static UINT mib2IpRoute[] = { 1,3,6,1,2,1,4,21,1 };
791 static PMIB_IPFORWARDTABLE ipRouteTable;
792
793 static struct structToAsnValue mib2IpRouteMap[] = {
794     { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardDest), copyIpAddr },
795     { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardIfIndex), copyInt },
796     { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardMetric1), copyInt },
797     { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardMetric2), copyInt },
798     { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardMetric3), copyInt },
799     { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardMetric4), copyInt },
800     { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardNextHop), copyIpAddr },
801     { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardType), copyInt },
802     { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardProto), copyInt },
803     { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardAge), copyInt },
804     { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardMask), copyIpAddr },
805     { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardMetric5), copyInt },
806 };
807
808 static void mib2IpRouteInit(void)
809 {
810     DWORD size = 0, ret = GetIpForwardTable(NULL, &size, TRUE);
811
812     if (ret == ERROR_INSUFFICIENT_BUFFER)
813     {
814         MIB_IPFORWARDTABLE *table = HeapAlloc(GetProcessHeap(), 0, size);
815         if (table)
816         {
817             if (!GetIpForwardTable(table, &size, TRUE)) ipRouteTable = table;
818             else HeapFree(GetProcessHeap(), 0, table );
819         }
820     }
821 }
822
823 static void mib2IpRouteCleanup(void)
824 {
825     HeapFree(GetProcessHeap(), 0, ipRouteTable);
826 }
827
828 static void oidToIpForwardRow(AsnObjectIdentifier *oid, void *dst)
829 {
830     MIB_IPFORWARDROW *row = dst;
831
832     row->dwForwardDest = oidToIpAddr(oid);
833 }
834
835 static int compareIpForwardRow(const void *a, const void *b)
836 {
837     const MIB_IPFORWARDROW *key = a, *value = b;
838
839     return key->dwForwardDest - value->dwForwardDest;
840 }
841
842 static BOOL mib2IpRouteQuery(BYTE bPduType, SnmpVarBind *pVarBind,
843     AsnInteger32 *pErrorStatus)
844 {
845     AsnObjectIdentifier myOid = DEFINE_OID(mib2IpRoute);
846     UINT tableIndex = 0, item = 0;
847     BOOL ret = TRUE;
848
849     TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name),
850         pErrorStatus);
851
852     switch (bPduType)
853     {
854     case SNMP_PDU_GET:
855     case SNMP_PDU_GETNEXT:
856         *pErrorStatus = getItemAndInstanceFromTable(&pVarBind->name,
857             &myOid, 4, bPduType, (struct GenericTable *)ipRouteTable,
858             sizeof(MIB_IPFORWARDROW), oidToIpForwardRow, compareIpForwardRow,
859             &item, &tableIndex);
860         if (!*pErrorStatus)
861         {
862             assert(tableIndex);
863             assert(item);
864             *pErrorStatus = mapStructEntryToValue(mib2IpRouteMap,
865                 DEFINE_SIZEOF(mib2IpRouteMap),
866                 &ipRouteTable->table[tableIndex - 1], item, bPduType, pVarBind);
867             if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT)
868                 ret = setOidWithItemAndIpAddr(&pVarBind->name, &myOid, item,
869                     ipRouteTable->table[tableIndex - 1].dwForwardDest);
870         }
871         break;
872     case SNMP_PDU_SET:
873         *pErrorStatus = SNMP_ERRORSTATUS_READONLY;
874         ret = FALSE;
875         break;
876     default:
877         FIXME("0x%02x: unsupported PDU type\n", bPduType);
878         *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
879     }
880     return ret;
881 }
882
883 static UINT mib2IpNet[] = { 1,3,6,1,2,1,4,22,1 };
884 static PMIB_IPNETTABLE ipNetTable;
885
886 static struct structToAsnValue mib2IpNetMap[] = {
887     { FIELD_OFFSET(MIB_IPNETROW, dwIndex), copyInt },
888     { FIELD_OFFSET(MIB_IPNETROW, dwPhysAddrLen), copyLengthPrecededString },
889     { FIELD_OFFSET(MIB_IPNETROW, dwAddr), copyIpAddr },
890     { FIELD_OFFSET(MIB_IPNETROW, dwType), copyInt },
891 };
892
893 static void mib2IpNetInit(void)
894 {
895     DWORD size = 0, ret = GetIpNetTable(NULL, &size, FALSE);
896
897     if (ret == ERROR_INSUFFICIENT_BUFFER)
898     {
899         MIB_IPNETTABLE *table = HeapAlloc(GetProcessHeap(), 0, size);
900         if (table)
901         {
902             if (!GetIpNetTable(table, &size, FALSE)) ipNetTable = table;
903             else HeapFree(GetProcessHeap(), 0, table );
904         }
905     }
906 }
907
908 static void mib2IpNetCleanup(void)
909 {
910     HeapFree(GetProcessHeap(), 0, ipNetTable);
911 }
912
913 static BOOL mib2IpNetQuery(BYTE bPduType, SnmpVarBind *pVarBind,
914     AsnInteger32 *pErrorStatus)
915 {
916     AsnObjectIdentifier myOid = DEFINE_OID(mib2IpNet);
917     BOOL ret = TRUE;
918
919     TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name),
920         pErrorStatus);
921
922     switch (bPduType)
923     {
924     case SNMP_PDU_GET:
925     case SNMP_PDU_GETNEXT:
926         if (!ipNetTable)
927             *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
928         else
929         {
930             UINT tableIndex = 0, item = 0;
931
932             *pErrorStatus = getItemAndIntegerInstanceFromOid(&pVarBind->name,
933                 &myOid, bPduType, &item, &tableIndex);
934             if (!*pErrorStatus)
935             {
936                 assert(tableIndex);
937                 assert(item);
938                 if (tableIndex > ipNetTable->dwNumEntries)
939                     *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
940                 else
941                 {
942                     *pErrorStatus = mapStructEntryToValue(mib2IpNetMap,
943                         DEFINE_SIZEOF(mib2IpNetMap),
944                         &ipNetTable[tableIndex - 1], item, bPduType, pVarBind);
945                     if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT)
946                         ret = setOidWithItemAndInteger(&pVarBind->name, &myOid,
947                             item, tableIndex);
948                 }
949             }
950         }
951         break;
952     case SNMP_PDU_SET:
953         *pErrorStatus = SNMP_ERRORSTATUS_READONLY;
954         ret = FALSE;
955         break;
956     default:
957         FIXME("0x%02x: unsupported PDU type\n", bPduType);
958         *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
959     }
960     return ret;
961 }
962
963 static UINT mib2Icmp[] = { 1,3,6,1,2,1,5 };
964 static MIB_ICMP icmpStats;
965
966 static void mib2IcmpInit(void)
967 {
968     GetIcmpStatistics(&icmpStats);
969 }
970
971 static struct structToAsnValue mib2IcmpMap[] = {
972     { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwMsgs), copyInt },
973     { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwErrors), copyInt },
974     { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwDestUnreachs), copyInt },
975     { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwTimeExcds), copyInt },
976     { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwParmProbs), copyInt },
977     { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwSrcQuenchs), copyInt },
978     { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwRedirects), copyInt },
979     { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwEchos), copyInt },
980     { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwEchoReps), copyInt },
981     { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwTimestamps), copyInt },
982     { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwTimestampReps), copyInt },
983     { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwAddrMasks), copyInt },
984     { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwAddrMaskReps), copyInt },
985     { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwMsgs), copyInt },
986     { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwErrors), copyInt },
987     { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwDestUnreachs), copyInt },
988     { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwTimeExcds), copyInt },
989     { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwParmProbs), copyInt },
990     { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwSrcQuenchs), copyInt },
991     { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwRedirects), copyInt },
992     { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwEchos), copyInt },
993     { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwEchoReps), copyInt },
994     { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwTimestamps), copyInt },
995     { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwTimestampReps), copyInt },
996     { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwAddrMasks), copyInt },
997     { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwAddrMaskReps), copyInt },
998 };
999
1000 static BOOL mib2IcmpQuery(BYTE bPduType, SnmpVarBind *pVarBind,
1001     AsnInteger32 *pErrorStatus)
1002 {
1003     AsnObjectIdentifier myOid = DEFINE_OID(mib2Icmp);
1004     UINT item = 0;
1005     BOOL ret = TRUE;
1006
1007     TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name),
1008         pErrorStatus);
1009
1010     switch (bPduType)
1011     {
1012     case SNMP_PDU_GET:
1013     case SNMP_PDU_GETNEXT:
1014         *pErrorStatus = getItemFromOid(&pVarBind->name, &myOid, bPduType,
1015             &item);
1016         if (!*pErrorStatus)
1017         {
1018             *pErrorStatus = mapStructEntryToValue(mib2IcmpMap,
1019                 DEFINE_SIZEOF(mib2IcmpMap), &icmpStats, item, bPduType,
1020                 pVarBind);
1021             if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT)
1022                 ret = setOidWithItem(&pVarBind->name, &myOid, item);
1023         }
1024         break;
1025     case SNMP_PDU_SET:
1026         *pErrorStatus = SNMP_ERRORSTATUS_READONLY;
1027         ret = FALSE;
1028         break;
1029     default:
1030         FIXME("0x%02x: unsupported PDU type\n", bPduType);
1031         *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
1032     }
1033     return ret;
1034 }
1035
1036 static UINT mib2Tcp[] = { 1,3,6,1,2,1,6 };
1037 static MIB_TCPSTATS tcpStats;
1038
1039 static void mib2TcpInit(void)
1040 {
1041     GetTcpStatistics(&tcpStats);
1042 }
1043
1044 static struct structToAsnValue mib2TcpMap[] = {
1045     { FIELD_OFFSET(MIB_TCPSTATS, dwRtoAlgorithm), copyInt },
1046     { FIELD_OFFSET(MIB_TCPSTATS, dwRtoMin), copyInt },
1047     { FIELD_OFFSET(MIB_TCPSTATS, dwRtoMax), copyInt },
1048     { FIELD_OFFSET(MIB_TCPSTATS, dwMaxConn), copyInt },
1049     { FIELD_OFFSET(MIB_TCPSTATS, dwActiveOpens), copyInt },
1050     { FIELD_OFFSET(MIB_TCPSTATS, dwPassiveOpens), copyInt },
1051     { FIELD_OFFSET(MIB_TCPSTATS, dwAttemptFails), copyInt },
1052     { FIELD_OFFSET(MIB_TCPSTATS, dwEstabResets), copyInt },
1053     { FIELD_OFFSET(MIB_TCPSTATS, dwCurrEstab), copyInt },
1054     { FIELD_OFFSET(MIB_TCPSTATS, dwInSegs), copyInt },
1055     { FIELD_OFFSET(MIB_TCPSTATS, dwOutSegs), copyInt },
1056     { FIELD_OFFSET(MIB_TCPSTATS, dwRetransSegs), copyInt },
1057     { FIELD_OFFSET(MIB_TCPSTATS, dwInErrs), copyInt },
1058     { FIELD_OFFSET(MIB_TCPSTATS, dwOutRsts), copyInt },
1059     { FIELD_OFFSET(MIB_TCPSTATS, dwNumConns), copyInt },
1060 };
1061
1062 static BOOL mib2TcpQuery(BYTE bPduType, SnmpVarBind *pVarBind,
1063     AsnInteger32 *pErrorStatus)
1064 {
1065     AsnObjectIdentifier myOid = DEFINE_OID(mib2Tcp);
1066     UINT item = 0;
1067     BOOL ret = TRUE;
1068
1069     TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name),
1070         pErrorStatus);
1071
1072     switch (bPduType)
1073     {
1074     case SNMP_PDU_GET:
1075     case SNMP_PDU_GETNEXT:
1076         *pErrorStatus = getItemFromOid(&pVarBind->name, &myOid, bPduType,
1077             &item);
1078         if (!*pErrorStatus)
1079         {
1080             *pErrorStatus = mapStructEntryToValue(mib2TcpMap,
1081                 DEFINE_SIZEOF(mib2TcpMap), &tcpStats, item, bPduType, pVarBind);
1082             if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT)
1083                 ret = setOidWithItem(&pVarBind->name, &myOid, item);
1084         }
1085         break;
1086     case SNMP_PDU_SET:
1087         *pErrorStatus = SNMP_ERRORSTATUS_READONLY;
1088         ret = FALSE;
1089         break;
1090     default:
1091         FIXME("0x%02x: unsupported PDU type\n", bPduType);
1092         *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
1093     }
1094     return ret;
1095 }
1096
1097 static UINT mib2Udp[] = { 1,3,6,1,2,1,7 };
1098 static MIB_UDPSTATS udpStats;
1099
1100 static void mib2UdpInit(void)
1101 {
1102     GetUdpStatistics(&udpStats);
1103 }
1104
1105 static struct structToAsnValue mib2UdpMap[] = {
1106     { FIELD_OFFSET(MIB_UDPSTATS, dwInDatagrams), copyInt },
1107     { FIELD_OFFSET(MIB_UDPSTATS, dwNoPorts), copyInt },
1108     { FIELD_OFFSET(MIB_UDPSTATS, dwInErrors), copyInt },
1109     { FIELD_OFFSET(MIB_UDPSTATS, dwOutDatagrams), copyInt },
1110 };
1111
1112 static BOOL mib2UdpQuery(BYTE bPduType, SnmpVarBind *pVarBind,
1113     AsnInteger32 *pErrorStatus)
1114 {
1115     AsnObjectIdentifier myOid = DEFINE_OID(mib2Udp);
1116     UINT item;
1117     BOOL ret = TRUE;
1118
1119     TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name),
1120         pErrorStatus);
1121
1122     switch (bPduType)
1123     {
1124     case SNMP_PDU_GET:
1125     case SNMP_PDU_GETNEXT:
1126         *pErrorStatus = getItemFromOid(&pVarBind->name, &myOid, bPduType,
1127             &item);
1128         if (!*pErrorStatus)
1129         {
1130             *pErrorStatus = mapStructEntryToValue(mib2UdpMap,
1131                 DEFINE_SIZEOF(mib2UdpMap), &udpStats, item, bPduType, pVarBind);
1132             if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT)
1133                 ret = setOidWithItem(&pVarBind->name, &myOid, item);
1134         }
1135         break;
1136     case SNMP_PDU_SET:
1137         *pErrorStatus = SNMP_ERRORSTATUS_READONLY;
1138         ret = FALSE;
1139         break;
1140     default:
1141         FIXME("0x%02x: unsupported PDU type\n", bPduType);
1142         *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
1143     }
1144     return ret;
1145 }
1146
1147 static UINT mib2UdpEntry[] = { 1,3,6,1,2,1,7,5,1 };
1148 static PMIB_UDPTABLE udpTable;
1149
1150 static void mib2UdpEntryInit(void)
1151 {
1152     DWORD size = 0, ret = GetUdpTable(NULL, &size, TRUE);
1153
1154     if (ret == ERROR_INSUFFICIENT_BUFFER)
1155     {
1156         MIB_UDPTABLE *table = HeapAlloc(GetProcessHeap(), 0, size);
1157         if (table)
1158         {
1159             if (!GetUdpTable(table, &size, TRUE)) udpTable = table;
1160             else HeapFree(GetProcessHeap(), 0, table);
1161         }
1162     }
1163 }
1164
1165 static void mib2UdpEntryCleanup(void)
1166 {
1167     HeapFree(GetProcessHeap(), 0, udpTable);
1168 }
1169
1170 static struct structToAsnValue mib2UdpEntryMap[] = {
1171     { FIELD_OFFSET(MIB_UDPROW, dwLocalAddr), copyIpAddr },
1172     { FIELD_OFFSET(MIB_UDPROW, dwLocalPort), copyInt },
1173 };
1174
1175 static void oidToUdpRow(AsnObjectIdentifier *oid, void *dst)
1176 {
1177     MIB_UDPROW *row = dst;
1178
1179     assert(oid && oid->idLength >= 5);
1180     row->dwLocalAddr = oidToIpAddr(oid);
1181     row->dwLocalPort = oid->ids[4];
1182 }
1183
1184 static int compareUdpRow(const void *a, const void *b)
1185 {
1186     const MIB_UDPROW *key = a, *value = b;
1187     int ret;
1188
1189     ret = key->dwLocalAddr - value->dwLocalAddr;
1190     if (ret == 0)
1191         ret = key->dwLocalPort - value->dwLocalPort;
1192     return ret;
1193 }
1194
1195 static BOOL mib2UdpEntryQuery(BYTE bPduType, SnmpVarBind *pVarBind,
1196     AsnInteger32 *pErrorStatus)
1197 {
1198     AsnObjectIdentifier myOid = DEFINE_OID(mib2UdpEntry);
1199     BOOL ret = TRUE;
1200
1201     TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name),
1202         pErrorStatus);
1203
1204     switch (bPduType)
1205     {
1206     case SNMP_PDU_GET:
1207     case SNMP_PDU_GETNEXT:
1208         if (!udpTable)
1209             *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
1210         else
1211         {
1212             UINT tableIndex = 0, item = 0;
1213
1214             *pErrorStatus = getItemAndInstanceFromTable(&pVarBind->name, &myOid,
1215                 5, bPduType, (struct GenericTable *)udpTable,
1216                 sizeof(MIB_UDPROW), oidToUdpRow, compareUdpRow, &item,
1217                 &tableIndex);
1218             if (!*pErrorStatus)
1219             {
1220                 assert(tableIndex);
1221                 assert(item);
1222                 *pErrorStatus = mapStructEntryToValue(mib2UdpEntryMap,
1223                     DEFINE_SIZEOF(mib2UdpEntryMap),
1224                     &udpTable->table[tableIndex - 1], item, bPduType, pVarBind);
1225                 if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT)
1226                 {
1227                     AsnObjectIdentifier oid;
1228
1229                     ret = setOidWithItemAndIpAddr(&pVarBind->name, &myOid, item,
1230                         udpTable->table[tableIndex - 1].dwLocalAddr);
1231                     if (ret)
1232                     {
1233                         oid.idLength = 1;
1234                         oid.ids = &udpTable->table[tableIndex - 1].dwLocalPort;
1235                         ret = SnmpUtilOidAppend(&pVarBind->name, &oid);
1236                     }
1237                 }
1238             }
1239         }
1240         break;
1241     case SNMP_PDU_SET:
1242         *pErrorStatus = SNMP_ERRORSTATUS_READONLY;
1243         ret = FALSE;
1244         break;
1245     default:
1246         FIXME("0x%02x: unsupported PDU type\n", bPduType);
1247         *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
1248     }
1249     return ret;
1250 }
1251
1252 /* This list MUST BE lexicographically sorted */
1253 static struct mibImplementation supportedIDs[] = {
1254     { DEFINE_OID(mib2IfNumber), mib2IfNumberInit, mib2IfNumberQuery,
1255       mib2IfNumberCleanup },
1256     { DEFINE_OID(mib2IfEntry), NULL, mib2IfEntryQuery, NULL },
1257     { DEFINE_OID(mib2Ip), mib2IpStatsInit, mib2IpStatsQuery, NULL },
1258     { DEFINE_OID(mib2IpAddr), mib2IpAddrInit, mib2IpAddrQuery,
1259       mib2IpAddrCleanup },
1260     { DEFINE_OID(mib2IpRoute), mib2IpRouteInit, mib2IpRouteQuery,
1261       mib2IpRouteCleanup },
1262     { DEFINE_OID(mib2IpNet), mib2IpNetInit, mib2IpNetQuery, mib2IpNetCleanup },
1263     { DEFINE_OID(mib2Icmp), mib2IcmpInit, mib2IcmpQuery, NULL },
1264     { DEFINE_OID(mib2Tcp), mib2TcpInit, mib2TcpQuery, NULL },
1265     { DEFINE_OID(mib2Udp), mib2UdpInit, mib2UdpQuery, NULL },
1266     { DEFINE_OID(mib2UdpEntry), mib2UdpEntryInit, mib2UdpEntryQuery,
1267       mib2UdpEntryCleanup },
1268 };
1269 static UINT minSupportedIDLength;
1270
1271 /*****************************************************************************
1272  * SnmpExtensionInit [INETMIB1.@]
1273  */
1274 BOOL WINAPI SnmpExtensionInit(DWORD dwUptimeReference,
1275     HANDLE *phSubagentTrapEvent, AsnObjectIdentifier *pFirstSupportedRegion)
1276 {
1277     AsnObjectIdentifier myOid = DEFINE_OID(mib2System);
1278     UINT i;
1279
1280     TRACE("(%d, %p, %p)\n", dwUptimeReference, phSubagentTrapEvent,
1281         pFirstSupportedRegion);
1282
1283     minSupportedIDLength = UINT_MAX;
1284     for (i = 0; i < sizeof(supportedIDs) / sizeof(supportedIDs[0]); i++)
1285     {
1286         if (supportedIDs[i].init)
1287             supportedIDs[i].init();
1288         if (supportedIDs[i].name.idLength < minSupportedIDLength)
1289             minSupportedIDLength = supportedIDs[i].name.idLength;
1290     }
1291     *phSubagentTrapEvent = NULL;
1292     SnmpUtilOidCpy(pFirstSupportedRegion, &myOid);
1293     return TRUE;
1294 }
1295
1296 static void cleanup(void)
1297 {
1298     UINT i;
1299
1300     for (i = 0; i < sizeof(supportedIDs) / sizeof(supportedIDs[0]); i++)
1301         if (supportedIDs[i].cleanup)
1302             supportedIDs[i].cleanup();
1303 }
1304
1305 static struct mibImplementation *findSupportedQuery(UINT *ids, UINT idLength,
1306     UINT *matchingIndex)
1307 {
1308     int indexHigh = DEFINE_SIZEOF(supportedIDs) - 1, indexLow = 0, i;
1309     struct mibImplementation *impl = NULL;
1310     AsnObjectIdentifier oid1 = { idLength, ids};
1311
1312     if (!idLength)
1313         return NULL;
1314     for (i = (indexLow + indexHigh) / 2; !impl && indexLow <= indexHigh;
1315          i = (indexLow + indexHigh) / 2)
1316     {
1317         INT cmp;
1318
1319         cmp = SnmpUtilOidNCmp(&oid1, &supportedIDs[i].name, idLength);
1320         if (!cmp)
1321         {
1322             impl = &supportedIDs[i];
1323             *matchingIndex = i;
1324         }
1325         else if (cmp > 0)
1326             indexLow = i + 1;
1327         else
1328             indexHigh = i - 1;
1329     }
1330     return impl;
1331 }
1332
1333 /*****************************************************************************
1334  * SnmpExtensionQuery [INETMIB1.@]
1335  */
1336 BOOL WINAPI SnmpExtensionQuery(BYTE bPduType, SnmpVarBindList *pVarBindList,
1337     AsnInteger32 *pErrorStatus, AsnInteger32 *pErrorIndex)
1338 {
1339     AsnObjectIdentifier mib2oid = DEFINE_OID(mib2);
1340     AsnInteger32 error = SNMP_ERRORSTATUS_NOERROR, errorIndex = 0;
1341     UINT i;
1342     BOOL ret = TRUE;
1343
1344     TRACE("(0x%02x, %p, %p, %p)\n", bPduType, pVarBindList,
1345         pErrorStatus, pErrorIndex);
1346
1347     for (i = 0; !error && i < pVarBindList->len; i++)
1348     {
1349         /* Ignore any OIDs not in MIB2 */
1350         if (!SnmpUtilOidNCmp(&pVarBindList->list[i].name, &mib2oid,
1351             mib2oid.idLength))
1352         {
1353             struct mibImplementation *impl = NULL;
1354             UINT len, matchingIndex = 0;
1355
1356             TRACE("%s\n", SnmpUtilOidToA(&pVarBindList->list[i].name));
1357             /* Search for an implementation matching as many octets as possible
1358              */
1359             for (len = pVarBindList->list[i].name.idLength;
1360                 len >= minSupportedIDLength && !impl; len--)
1361                 impl = findSupportedQuery(pVarBindList->list[i].name.ids, len,
1362                     &matchingIndex);
1363             if (impl && impl->query)
1364                 ret = impl->query(bPduType, &pVarBindList->list[i], &error);
1365             else
1366                 error = SNMP_ERRORSTATUS_NOSUCHNAME;
1367             if (error == SNMP_ERRORSTATUS_NOSUCHNAME &&
1368                 bPduType == SNMP_PDU_GETNEXT)
1369             {
1370                 /* GetNext is special: it finds the successor to the given OID,
1371                  * so we have to continue until an implementation handles the
1372                  * query or we exhaust the table of supported OIDs.
1373                  */
1374                 for (matchingIndex++; error == SNMP_ERRORSTATUS_NOSUCHNAME &&
1375                     matchingIndex < DEFINE_SIZEOF(supportedIDs);
1376                     matchingIndex++)
1377                 {
1378                     error = SNMP_ERRORSTATUS_NOERROR;
1379                     impl = &supportedIDs[matchingIndex];
1380                     if (impl->query)
1381                         ret = impl->query(bPduType, &pVarBindList->list[i],
1382                             &error);
1383                     else
1384                         error = SNMP_ERRORSTATUS_NOSUCHNAME;
1385                 }
1386                 /* If the query still isn't resolved, set the OID to the
1387                  * successor to the last entry in the table.
1388                  */
1389                 if (error == SNMP_ERRORSTATUS_NOSUCHNAME)
1390                 {
1391                     SnmpUtilOidFree(&pVarBindList->list[i].name);
1392                     ret = SnmpUtilOidCpy(&pVarBindList->list[i].name,
1393                         &supportedIDs[matchingIndex - 1].name);
1394                     pVarBindList->list[i].name.ids[
1395                         pVarBindList->list[i].name.idLength - 1] += 1;
1396                 }
1397             }
1398             if (error)
1399                 errorIndex = i + 1;
1400         }
1401     }
1402     *pErrorStatus = error;
1403     *pErrorIndex = errorIndex;
1404     return ret;
1405 }
1406
1407 /*****************************************************************************
1408  * DllMain [INETMIB1.@]
1409  */
1410 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
1411 {
1412     TRACE("(0x%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved);
1413
1414     switch (fdwReason)
1415     {
1416         case DLL_PROCESS_ATTACH:
1417             DisableThreadLibraryCalls(hinstDLL);
1418             break;
1419         case DLL_PROCESS_DETACH:
1420             cleanup();
1421             break;
1422         default:
1423             break;
1424     }
1425
1426     return TRUE;
1427 }