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