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